[{"content":"Bit Twiddling Hacks 常用位运算技巧总结 来源整理自 Bit Twiddling Hacks，这里只归纳工程里最常见、最实用的一批技巧。\n风格保持和 common-algorithm-patterns.md 一致：每个技巧只保留 解释 + 代码框架（C++）。\n0) 使用前提（很重要） 解释 位运算技巧很依赖整数表示和移位语义。为了减少未定义行为和平台差异：\n优先使用 uint32_t/uint64_t 等无符号类型做位运算。 右移有符号负数在标准里是实现定义（不同编译器/架构可能不同）。 涉及溢出的表达式（如 x - y）要先考虑范围。 代码框架 1 2 3 4 5 6 7 #include \u0026lt;bit\u0026gt; #include \u0026lt;cstdint\u0026gt; #include \u0026lt;limits\u0026gt; using u32 = uint32_t; using u64 = uint64_t; using i32 = int32_t; 1) 判断符号 / 是否异号 解释 符号：常见返回 -1/0/1 三值。 异号：两个数符号位不同，则 x ^ y 的最高位为 1（在补码机器上很常用）。 代码框架 1 2 3 4 5 6 7 8 9 10 11 #include \u0026lt;cstdint\u0026gt; int sign3(int x) { // 返回 -1 / 0 / 1，分支少且可读性高 return (x \u0026gt; 0) - (x \u0026lt; 0); } bool oppositeSigns(int x, int y) { // 常见位技巧（依赖补码/符号位语义） return (x ^ y) \u0026lt; 0; } 2) 无分支 abs / min / max 解释 这类写法常见于追求分支预测稳定性的场景。现代编译器对普通 std::abs/std::min/std::max 也很强，实战要基准测试后再决定是否上 hack。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include \u0026lt;cstdint\u0026gt; int absNoBranch(int v) { int mask = v \u0026gt;\u0026gt; 31; // 若 v\u0026lt;0，mask 全1；否则全0（实现相关） return (v ^ mask) - mask; // 或 (v + mask) ^ mask } int minNoBranch(int x, int y) { return y ^ ((x ^ y) \u0026amp; -(x \u0026lt; y)); } int maxNoBranch(int x, int y) { return x ^ ((x ^ y) \u0026amp; -(x \u0026lt; y)); } 3) 判断 2 的幂 / 提取 lowbit 解释 x \u0026amp; (x - 1) 会清掉最低位的 1。 因此 x != 0 \u0026amp;\u0026amp; (x \u0026amp; (x - 1)) == 0 表示只有一位为 1，即 2 的幂。 x \u0026amp; -x 可提取最低位 1（Fenwick 树等常用）。 代码框架 1 2 3 4 5 6 7 8 9 #include \u0026lt;cstdint\u0026gt; bool isPowerOfTwo(uint32_t x) { return x \u0026amp;\u0026amp; ((x \u0026amp; (x - 1)) == 0); } uint32_t lowbit(uint32_t x) { return x \u0026amp; (~x + 1); // 等价于 x \u0026amp; -x } 3.5) 异或核心恒等式（a ^ a = 0）与两道高频题模板 解释 ref/fucking-algorithm/算法思维系列/常用的位操作.md 里非常强调这一组性质，它确实是算法题高频：\na ^ a = 0 a ^ 0 = a 异或满足交换律/结合律 这三条足够解决大量“成对出现 + 一个落单”类题目。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include \u0026lt;vector\u0026gt; using namespace std; int singleNumber(const vector\u0026lt;int\u0026gt;\u0026amp; nums) { int x = 0; for (int v : nums) x ^= v; return x; } int missingNumber(const vector\u0026lt;int\u0026gt;\u0026amp; nums) { // nums 长度为 n，元素应覆盖 [0..n] 且缺一个 int n = (int)nums.size(); int x = n; for (int i = 0; i \u0026lt; n; ++i) { x ^= i ^ nums[i]; } return x; } 3.6) index \u0026amp; (len - 1) 环形数组技巧 解释 你引用的那篇里这个技巧也很实用：当 len 是 2 的幂时，index \u0026amp; (len - 1) 等价于 index % len。\n适合 ring buffer / 循环数组热路径。若 len 不是 2 的幂，这个技巧不成立。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;cassert\u0026gt; #include \u0026lt;cstdint\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; bool isPowerOfTwo(size_t x) { return x \u0026amp;\u0026amp; ((x \u0026amp; (x - 1)) == 0); } int circularAt(const vector\u0026lt;int\u0026gt;\u0026amp; arr, int64_t index) { size_t n = arr.size(); assert(isPowerOfTwo(n)); // 前提条件 size_t pos = static_cast\u0026lt;size_t\u0026gt;(index) \u0026amp; (n - 1); return arr[pos]; } 4) 条件置位 / 清位 / 按掩码合并 解释 这类技巧用于“按条件更新某些位”，避免 if 分支。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 #include \u0026lt;cstdint\u0026gt; uint32_t setOrClearByFlag(uint32_t w, uint32_t mask, bool flag) { // flag=true -\u0026gt; 置位；false -\u0026gt; 清位 // 常见分支消除写法 return (w \u0026amp; ~mask) | (static_cast\u0026lt;uint32_t\u0026gt;(-static_cast\u0026lt;int\u0026gt;(flag)) \u0026amp; mask); } uint32_t mergeByMask(uint32_t a, uint32_t b, uint32_t mask) { // mask 为 1 的位来自 b，否则来自 a return (a \u0026amp; ~mask) | (b \u0026amp; mask); } 4.5) 位掩码状态操作（增删查改） 解释 在子集枚举、DP 状压、权限位里是必备模板：\n查第 k 位 置 1 / 清 0 / 翻转 清除最低位 1 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include \u0026lt;cstdint\u0026gt; bool testBit(uint32_t x, int k) { return (x \u0026gt;\u0026gt; k) \u0026amp; 1u; } uint32_t setBit(uint32_t x, int k) { return x | (1u \u0026lt;\u0026lt; k); } uint32_t clearBit(uint32_t x, int k) { return x \u0026amp; ~(1u \u0026lt;\u0026lt; k); } uint32_t toggleBit(uint32_t x, int k) { return x ^ (1u \u0026lt;\u0026lt; k); } uint32_t clearLowestOne(uint32_t x) { return x \u0026amp; (x - 1); } 5) 统计 1 的个数（popcount） 解释 最常用两种：\nKernighan：每轮消一个 1，复杂度和 1 的个数相关。 std::popcount：C++20 标准库，通常会映射到 CPU 指令。 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;bit\u0026gt; #include \u0026lt;cstdint\u0026gt; int popcountKernighan(uint32_t x) { int cnt = 0; while (x) { x \u0026amp;= (x - 1); ++cnt; } return cnt; } int popcountStd(uint32_t x) { return std::popcount(x); } 6) 奇偶校验（parity） 解释 parity = popcount(x) % 2。\n可用折叠异或快速压缩到 1 bit。\n代码框架 1 2 3 4 5 6 7 8 9 10 #include \u0026lt;cstdint\u0026gt; int parity32(uint32_t x) { x ^= x \u0026gt;\u0026gt; 16; x ^= x \u0026gt;\u0026gt; 8; x ^= x \u0026gt;\u0026gt; 4; x ^= x \u0026gt;\u0026gt; 2; x ^= x \u0026gt;\u0026gt; 1; return x \u0026amp; 1; // 1=奇数个1，0=偶数个1 } 7) 末尾 0 个数（ctz）/ 最高位位置（log2） 解释 ctz 常用于位图、稀疏状态、lowbit 跳转。 floor(log2(x)) 本质是最高位 1 的位置。 推荐 std::countr_zero / std::bit_width（C++20）。 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 #include \u0026lt;bit\u0026gt; #include \u0026lt;cstdint\u0026gt; int ctz32(uint32_t x) { if (x == 0) return 32; // 约定返回位宽 return std::countr_zero(x); } int floorLog2(uint32_t x) { // x \u0026gt; 0 return std::bit_width(x) - 1; } 8) 向上取整到最近 2 的幂 解释 经典“位扩散”技巧：先把最高位 1 右边全部填满，再 +1。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 #include \u0026lt;cstdint\u0026gt; uint32_t roundUpPow2(uint32_t x) { if (x \u0026lt;= 1) return 1; --x; x |= x \u0026gt;\u0026gt; 1; x |= x \u0026gt;\u0026gt; 2; x |= x \u0026gt;\u0026gt; 4; x |= x \u0026gt;\u0026gt; 8; x |= x \u0026gt;\u0026gt; 16; return x + 1; } 9) 位反转（bit reverse） 解释 常见于 FFT/图像/编码场景。\n简单写法是逐位搬运；追求极致性能可用查表或并行交换技巧。\n代码框架 1 2 3 4 5 6 7 8 9 10 #include \u0026lt;cstdint\u0026gt; uint32_t reverseBits32(uint32_t x) { uint32_t r = 0; for (int i = 0; i \u0026lt; 32; ++i) { r = (r \u0026lt;\u0026lt; 1) | (x \u0026amp; 1u); x \u0026gt;\u0026gt;= 1; } return r; } 10) 字（word）内字节检测（zero-byte trick） 解释 在一个 32/64 位整数里并行判断“某字节是否为 0”，常用于字符串扫描优化。\n经典式：((x - 0x0101...) \u0026amp; ~x \u0026amp; 0x8080...) != 0\n代码框架 1 2 3 4 5 #include \u0026lt;cstdint\u0026gt; bool hasZeroByte(uint32_t x) { return ((x - 0x01010101u) \u0026amp; ~x \u0026amp; 0x80808080u) != 0; } 11) 模 2^s 与模 (2^s - 1) 解释 x % (1 \u0026lt;\u0026lt; s) 可直接用掩码：x \u0026amp; ((1\u0026lt;\u0026lt;s)-1)。 % (2^s - 1) 有折叠求和技巧（页面有多版本），常见于特定编码/校验优化。 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include \u0026lt;cstdint\u0026gt; uint32_t modPow2(uint32_t x, unsigned s) { return x \u0026amp; ((1u \u0026lt;\u0026lt; s) - 1u); } uint32_t modPow2Minus1(uint32_t x, unsigned s) { // 通用折叠框架（非最优版，重在思路） uint32_t d = (1u \u0026lt;\u0026lt; s) - 1u; while (x \u0026gt; d) { x = (x \u0026amp; d) + (x \u0026gt;\u0026gt; s); } return (x == d) ? 0 : x; } 12) 下一个同 1 个数的排列（snoob） 解释 给定一个 bitmask，求字典序下一个且“1 的个数相同”的 bitmask。\n组合枚举、子集状态迭代里非常有用。\n代码框架 1 2 3 4 5 6 7 8 #include \u0026lt;cstdint\u0026gt; uint32_t nextBitPermutation(uint32_t v) { uint32_t c = v \u0026amp; -v; uint32_t r = v + c; if (r == 0) return 0; // 溢出，无下一个 return (((r ^ v) \u0026gt;\u0026gt; 2) / c) | r; } 13) 实战建议：优先标准库，再用 hacks 解释 bithacks 的核心价值是“理解位级等价变换”。但工程上推荐：\n先写可读版本； 优先 std::popcount/std::countr_zero/std::bit_width； 只在性能瓶颈处引入 hack，并附注释和基准测试结果。 代码框架 1 2 3 4 // 1) baseline 可读实现 // 2) profiler 发现热点 // 3) 替换成位技巧或 intrinsic // 4) 基准测试 + 单元测试（边界值：0、INT_MIN、全1、单bit） ","permalink":"https://yy-tech.online/zh/post/bit-twiddling-hacks-summary/","summary":"\u003ch1 id=\"bit-twiddling-hacks-常用位运算技巧总结\"\u003eBit Twiddling Hacks 常用位运算技巧总结\u003c/h1\u003e\n\u003cp\u003e来源整理自 \u003ca href=\"https://graphics.stanford.edu/~seander/bithacks.html\"\u003eBit Twiddling Hacks\u003c/a\u003e，这里只归纳工程里最常见、最实用的一批技巧。\u003cbr\u003e\n风格保持和 \u003ccode\u003ecommon-algorithm-patterns.md\u003c/code\u003e 一致：每个技巧只保留 \u003cstrong\u003e解释\u003c/strong\u003e + \u003cstrong\u003e代码框架（C++）\u003c/strong\u003e。\u003c/p\u003e","title":"Bit Twiddling Hacks 常用位运算技巧总结"},{"content":"常见算法套路 1) 框架思维（遍历是核心） 解释 很多题目本质是「遍历某种结构」：数组、链表、树、图。先确定数据结构，再确定遍历顺序，最后把业务判断填进遍历骨架。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include \u0026lt;vector\u0026gt; using namespace std; struct ListNode { int val; ListNode* next; }; struct TreeNode { int val; TreeNode* left; TreeNode* right; }; void processValue(int x) { // TODO: 业务处理 (void)x; } void traverseArray(const vector\u0026lt;int\u0026gt;\u0026amp; nums) { for (int i = 0; i \u0026lt; (int)nums.size(); ++i) { processValue(nums[i]); } } void traverseList(ListNode* head) { for (ListNode* p = head; p != nullptr; p = p-\u0026gt;next) { processValue(p-\u0026gt;val); } } void traverseTree(TreeNode* root) { if (root == nullptr) return; // 前序位置 processValue(root-\u0026gt;val); traverseTree(root-\u0026gt;left); traverseTree(root-\u0026gt;right); // 后序位置 } 2) 二分查找（缩小搜索空间） 解释 当问题满足单调性（答案在一边）时，用二分不断折半区间。关键在区间定义一致（闭区间或左闭右开）和边界收缩逻辑一致。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include \u0026lt;vector\u0026gt; using namespace std; int binarySearch(const vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { int left = 0, right = (int)nums.size() - 1; while (left \u0026lt;= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) return mid; if (nums[mid] \u0026lt; target) { left = mid + 1; } else { right = mid - 1; } } return -1; } int leftBound(const vector\u0026lt;int\u0026gt;\u0026amp; nums, int target) { int left = 0, right = (int)nums.size(); // [left, right) while (left \u0026lt; right) { int mid = left + (right - left) / 2; if (nums[mid] \u0026gt;= target) { right = mid; } else { left = mid + 1; } } if (left \u0026gt;= (int)nums.size() || nums[left] != target) return -1; return left; } 3) BFS（最短步数/层序扩散） 解释 BFS 用队列按层扩散，天然适合「最少操作次数」「最短路径（无权图）」问题。难点通常不是 BFS 本身，而是把题目状态抽象成图节点。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include \u0026lt;queue\u0026gt; #include \u0026lt;unordered_set\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; vector\u0026lt;int\u0026gt; neighbors(int state) { // TODO: 根据题意生成相邻状态 return {}; } int bfsMinStep(int start, int target) { queue\u0026lt;int\u0026gt; q; unordered_set\u0026lt;int\u0026gt; visited; q.push(start); visited.insert(start); int step = 0; while (!q.empty()) { int sz = (int)q.size(); for (int i = 0; i \u0026lt; sz; ++i) { int cur = q.front(); q.pop(); if (cur == target) return step; for (int nxt : neighbors(cur)) { if (!visited.count(nxt)) { visited.insert(nxt); q.push(nxt); } } } ++step; } return -1; } 4) 双向 BFS（两头夹逼） 解释 从起点和终点同时扩散，当前沿相遇时结束。通常比单向 BFS 更快，适合状态空间很大、且可以反向扩展的问题。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include \u0026lt;unordered_set\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; vector\u0026lt;int\u0026gt; neighbors(int state) { // TODO: 根据题意生成相邻状态 return {}; } int bidirectionalBfs(int start, int target) { if (start == target) return 0; unordered_set\u0026lt;int\u0026gt; front{start}, back{target}, visited{start, target}; int step = 0; while (!front.empty() \u0026amp;\u0026amp; !back.empty()) { if (front.size() \u0026gt; back.size()) swap(front, back); unordered_set\u0026lt;int\u0026gt; nextFront; for (int cur : front) { if (back.count(cur)) return step; for (int nxt : neighbors(cur)) { if (!visited.count(nxt)) { visited.insert(nxt); nextFront.insert(nxt); } } } front = move(nextFront); ++step; } return -1; } 5) 回溯（决策树穷举） 解释 回溯本质是 DFS 枚举决策树：做选择 -\u0026gt; 递归 -\u0026gt; 撤销选择。适用于排列、组合、子集、约束搜索等。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include \u0026lt;vector\u0026gt; using namespace std; vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; result; vector\u0026lt;int\u0026gt; path; vector\u0026lt;int\u0026gt; used; bool isValid(int choice) { // TODO: 剪枝条件 (void)choice; return true; } void backtrack(const vector\u0026lt;int\u0026gt;\u0026amp; nums) { if ((int)path.size() == (int)nums.size()) { result.push_back(path); return; } for (int i = 0; i \u0026lt; (int)nums.size(); ++i) { if (used[i]) continue; if (!isValid(nums[i])) continue; path.push_back(nums[i]); // 做选择 used[i] = 1; backtrack(nums); used[i] = 0; // 撤销选择 path.pop_back(); } } 6) 集合划分回溯（元素视角 / 桶视角） 解释 划分问题常见两种建模：\n元素视角：每个元素放入某个桶。 桶视角：逐个桶去装元素。\n配合剪枝（排序、超限剪枝、空桶去重）降低搜索量。 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include \u0026lt;algorithm\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; bool dfsPartition(int i, const vector\u0026lt;int\u0026gt;\u0026amp; nums, vector\u0026lt;int\u0026gt;\u0026amp; bucket, int target) { if (i == (int)nums.size()) { for (int s : bucket) { if (s != target) return false; } return true; } for (int j = 0; j \u0026lt; (int)bucket.size(); ++j) { if (bucket[j] + nums[i] \u0026gt; target) continue; if (j \u0026gt; 0 \u0026amp;\u0026amp; bucket[j] == bucket[j - 1]) continue; // 对称剪枝 bucket[j] += nums[i]; if (dfsPartition(i + 1, nums, bucket, target)) return true; bucket[j] -= nums[i]; } return false; } bool canPartitionKSubsets(vector\u0026lt;int\u0026gt; nums, int k) { int sum = 0; for (int x : nums) sum += x; if (k \u0026lt;= 0 || sum % k != 0) return false; int target = sum / k; sort(nums.rbegin(), nums.rend()); // 常见剪枝 vector\u0026lt;int\u0026gt; bucket(k, 0); return dfsPartition(0, nums, bucket, target); } 7) 滑动窗口（子串/子数组线性扫描） 解释 核心是维护一个可变窗口 [left, right)：右指针扩张找可行解，左指针收缩优化答案。适用于连续区间问题，常配计数器/哈希表。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include \u0026lt;climits\u0026gt; #include \u0026lt;string\u0026gt; #include \u0026lt;unordered_map\u0026gt; using namespace std; int slidingWindowTemplate(const string\u0026amp; s, const string\u0026amp; t) { unordered_map\u0026lt;char, int\u0026gt; need, window; for (char c : t) need[c]++; int left = 0, right = 0; int valid = 0; int bestLen = INT_MAX; while (right \u0026lt; (int)s.size()) { char c = s[right++]; if (need.count(c)) { window[c]++; if (window[c] == need[c]) valid++; } while (valid == (int)need.size()) { // TODO: 这里更新业务答案 bestLen = min(bestLen, right - left); char d = s[left++]; if (need.count(d)) { if (window[d] == need[d]) valid--; window[d]--; } } } return bestLen == INT_MAX ? -1 : bestLen; } 8) 双指针（快慢 / 左右） 解释 双指针用两个位置变量协同遍历：\n快慢指针：同向，常用于去重、原地删除、链表判环。 左右指针：相向，常用于有序数组配对、回文判断、反转。 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include \u0026lt;string\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; bool isValidValue(int x) { // TODO: 业务判断 return x \u0026gt;= 0; } int fastSlowFilter(vector\u0026lt;int\u0026gt;\u0026amp; nums) { int slow = 0; for (int fast = 0; fast \u0026lt; (int)nums.size(); ++fast) { if (isValidValue(nums[fast])) { nums[slow++] = nums[fast]; } } return slow; // [0, slow) 有效 } bool isPalindrome(const string\u0026amp; s) { int left = 0, right = (int)s.size() - 1; while (left \u0026lt; right) { if (s[left] != s[right]) return false; ++left; --right; } return true; } 9) 前缀和（快速求区间和） 解释 预处理累加信息后，用 O(1) 代价查询区间和。典型是「空间换时间」；一维和二维都适用。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include \u0026lt;vector\u0026gt; using namespace std; struct PrefixSum1D { vector\u0026lt;long long\u0026gt; pre; explicit PrefixSum1D(const vector\u0026lt;int\u0026gt;\u0026amp; nums) { pre.assign(nums.size() + 1, 0); for (int i = 1; i \u0026lt;= (int)nums.size(); ++i) { pre[i] = pre[i - 1] + nums[i - 1]; } } long long rangeSum(int l, int r) const { // [l, r] return pre[r + 1] - pre[l]; } }; struct PrefixSum2D { vector\u0026lt;vector\u0026lt;long long\u0026gt;\u0026gt; pre; explicit PrefixSum2D(const vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; mat) { int m = (int)mat.size(); int n = m ? (int)mat[0].size() : 0; pre.assign(m + 1, vector\u0026lt;long long\u0026gt;(n + 1, 0)); for (int i = 1; i \u0026lt;= m; ++i) { for (int j = 1; j \u0026lt;= n; ++j) { pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + mat[i - 1][j - 1]; } } } }; 10) 差分数组（区间增减高效更新） 解释 当「区间修改很多、最后统一求结果」时，用差分数组。对区间 [l, r] 加 val，只改两个端点；最后做前缀和还原原数组。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include \u0026lt;vector\u0026gt; using namespace std; struct Difference { vector\u0026lt;long long\u0026gt; diff; explicit Difference(int n) : diff(n, 0) {} void add(int l, int r, long long val) { diff[l] += val; if (r + 1 \u0026lt; (int)diff.size()) diff[r + 1] -= val; } vector\u0026lt;long long\u0026gt; result() const { vector\u0026lt;long long\u0026gt; nums(diff.size(), 0); if (diff.empty()) return nums; nums[0] = diff[0]; for (int i = 1; i \u0026lt; (int)diff.size(); ++i) { nums[i] = nums[i - 1] + diff[i]; } return nums; } }; 11) 并查集 Union-Find（连通分量） 解释 处理动态连通性：两个点是否连通、合并两个集合。常见优化是路径压缩 + 按秩/按大小合并。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include \u0026lt;vector\u0026gt; using namespace std; class UnionFind { public: explicit UnionFind(int n) : parent(n), sz(n, 1) { for (int i = 0; i \u0026lt; n; ++i) parent[i] = i; } int find(int x) { while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } return x; } void unite(int a, int b) { int ra = find(a), rb = find(b); if (ra == rb) return; if (sz[ra] \u0026lt; sz[rb]) swap(ra, rb); parent[rb] = ra; sz[ra] += sz[rb]; } bool connected(int a, int b) { return find(a) == find(b); } private: vector\u0026lt;int\u0026gt; parent; vector\u0026lt;int\u0026gt; sz; }; 12) 位运算（状态压缩与位级技巧） 解释 位运算适合做集合状态压缩、快速判定、去重和奇偶性/次数问题。常用恒等式：n \u0026amp; (n-1)、x ^ x = 0、位掩码测试与设置。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include \u0026lt;vector\u0026gt; using namespace std; bool isBitOne(int x, int k) { return ((x \u0026gt;\u0026gt; k) \u0026amp; 1) == 1; } int setBit(int x, int k) { return x | (1 \u0026lt;\u0026lt; k); } int clearBit(int x, int k) { return x \u0026amp; ~(1 \u0026lt;\u0026lt; k); } int hammingWeight(unsigned int x) { int cnt = 0; while (x != 0) { x \u0026amp;= (x - 1); ++cnt; } return cnt; } int singleNumber(const vector\u0026lt;int\u0026gt;\u0026amp; nums) { int ans = 0; for (int v : nums) ans ^= v; return ans; } 13) 矩阵花式遍历（边界控制 + 变换） 解释 二维题目常见两类套路：\n通过几何变换（转置、翻转）完成旋转。 通过四边界 top/bottom/left/right 控制螺旋遍历。 代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include \u0026lt;algorithm\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; void rotateClockwise90(vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; matrix) { int n = (int)matrix.size(); for (int i = 0; i \u0026lt; n; ++i) { for (int j = i + 1; j \u0026lt; n; ++j) { swap(matrix[i][j], matrix[j][i]); } } for (auto\u0026amp; row : matrix) { reverse(row.begin(), row.end()); } } vector\u0026lt;int\u0026gt; spiralOrder(const vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt;\u0026amp; mat) { vector\u0026lt;int\u0026gt; ans; if (mat.empty() || mat[0].empty()) return ans; int top = 0, bottom = (int)mat.size() - 1; int left = 0, right = (int)mat[0].size() - 1; while (top \u0026lt;= bottom \u0026amp;\u0026amp; left \u0026lt;= right) { for (int j = left; j \u0026lt;= right; ++j) ans.push_back(mat[top][j]); ++top; for (int i = top; i \u0026lt;= bottom; ++i) ans.push_back(mat[i][right]); --right; if (top \u0026lt;= bottom) { for (int j = right; j \u0026gt;= left; --j) ans.push_back(mat[bottom][j]); --bottom; } if (left \u0026lt;= right) { for (int i = bottom; i \u0026gt;= top; --i) ans.push_back(mat[i][left]); ++left; } } return ans; } 14) 字符串乘法（模拟竖式） 解释 大整数不能直接转内置整数时，按竖式逐位乘法。核心是结果数组下标映射和进位处理。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include \u0026lt;string\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; string multiplyStrings(const string\u0026amp; num1, const string\u0026amp; num2) { if (num1 == \u0026#34;0\u0026#34; || num2 == \u0026#34;0\u0026#34;) return \u0026#34;0\u0026#34;; int m = (int)num1.size(), n = (int)num2.size(); vector\u0026lt;int\u0026gt; res(m + n, 0); for (int i = m - 1; i \u0026gt;= 0; --i) { for (int j = n - 1; j \u0026gt;= 0; --j) { int mul = (num1[i] - \u0026#39;0\u0026#39;) * (num2[j] - \u0026#39;0\u0026#39;); int p1 = i + j, p2 = i + j + 1; int sum = mul + res[p2]; res[p2] = sum % 10; res[p1] += sum / 10; } } string ans; int i = 0; while (i \u0026lt; (int)res.size() \u0026amp;\u0026amp; res[i] == 0) ++i; for (; i \u0026lt; (int)res.size(); ++i) ans.push_back(char(\u0026#39;0\u0026#39; + res[i])); return ans.empty() ? \u0026#34;0\u0026#34; : ans; } 15) 洗牌算法（Fisher-Yates） 解释 要让所有排列等概率出现，必须在第 i 轮从 [i, n-1] 中等概率选一个位置交换。常用于随机打乱数组。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 #include \u0026lt;random\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; void shuffleArray(vector\u0026lt;int\u0026gt;\u0026amp; nums) { static random_device rd; static mt19937 gen(rd()); for (int i = 0; i \u0026lt; (int)nums.size(); ++i) { uniform_int_distribution\u0026lt;int\u0026gt; dist(i, (int)nums.size() - 1); int j = dist(gen); swap(nums[i], nums[j]); } } 16) 蒙特卡罗验证（随机算法自检） 解释 对随机算法可用大量重复实验做统计，观察频率分布是否接近期望分布。它不是证明，但可以快速发现明显偏差。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; #include \u0026lt;string\u0026gt; #include \u0026lt;unordered_map\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; void shuffleArrayForCheck(vector\u0026lt;int\u0026gt;\u0026amp; nums) { static random_device rd; static mt19937 gen(rd()); for (int i = 0; i \u0026lt; (int)nums.size(); ++i) { uniform_int_distribution\u0026lt;int\u0026gt; dist(i, (int)nums.size() - 1); int j = dist(gen); swap(nums[i], nums[j]); } } string stateKey(const vector\u0026lt;int\u0026gt;\u0026amp; arr) { string key; for (int x : arr) { key += to_string(x) + \u0026#34;,\u0026#34;; } return key; } void monteCarloCheck(vector\u0026lt;int\u0026gt; arr, int T) { unordered_map\u0026lt;string, int\u0026gt; counter; for (int i = 0; i \u0026lt; T; ++i) { vector\u0026lt;int\u0026gt; tmp = arr; shuffleArrayForCheck(tmp); counter[stateKey(tmp)]++; } // TODO: 根据 counter 做统计分析 cout \u0026lt;\u0026lt; \u0026#34;distinct states = \u0026#34; \u0026lt;\u0026lt; counter.size() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } 17) 递归排序思路：烧饼排序（翻转构造） 解释 把当前最大元素翻到最前，再翻到目标末尾，递归处理剩余前缀。属于「先解决一个元素，再缩小问题规模」的构造性递归套路。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include \u0026lt;algorithm\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; void flip(vector\u0026lt;int\u0026gt;\u0026amp; cakes, int i, int j) { while (i \u0026lt; j) swap(cakes[i++], cakes[j--]); } int indexOfMax(const vector\u0026lt;int\u0026gt;\u0026amp; cakes, int n) { int idx = 0; for (int i = 1; i \u0026lt; n; ++i) { if (cakes[i] \u0026gt; cakes[idx]) idx = i; } return idx; } void pancakeSortDfs(vector\u0026lt;int\u0026gt;\u0026amp; cakes, int n, vector\u0026lt;int\u0026gt;\u0026amp; ops) { if (n \u0026lt;= 1) return; int idx = indexOfMax(cakes, n); flip(cakes, 0, idx); ops.push_back(idx + 1); flip(cakes, 0, n - 1); ops.push_back(n); pancakeSortDfs(cakes, n - 1, ops); } 18) 概率问题建模（拆分样本空间） 解释 概率题常见误区是凭直觉。应先定义实验过程，再写清样本空间和条件事件，最后按条件概率/全概率公式计算。\n代码框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include \u0026lt;functional\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; struct EventCase { double pCase; double pEventGivenCase; }; double totalProbability(const vector\u0026lt;EventCase\u0026gt;\u0026amp; cases) { double ans = 0.0; for (const auto\u0026amp; c : cases) { ans += c.pCase * c.pEventGivenCase; } return ans; } double conditionalProbability(double pEandC, double pC) { if (pC == 0.0) return 0.0; // 或抛异常 return pEandC / pC; } ","permalink":"https://yy-tech.online/zh/post/common-algorithm-patterns/","summary":"\u003ch1 id=\"常见算法套路\"\u003e常见算法套路\u003c/h1\u003e\n\u003ch2 id=\"1-框架思维遍历是核心\"\u003e1) 框架思维（遍历是核心）\u003c/h2\u003e\n\u003ch3 id=\"解释\"\u003e解释\u003c/h3\u003e\n\u003cp\u003e很多题目本质是「遍历某种结构」：数组、链表、树、图。先确定数据结构，再确定遍历顺序，最后把业务判断填进遍历骨架。\u003c/p\u003e\n\u003ch3 id=\"代码框架\"\u003e代码框架\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e36\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e37\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e38\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e39\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;vector\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eusing\u003c/span\u003e \u003cspan class=\"k\"\u003enamespace\u003c/span\u003e \u003cspan class=\"n\"\u003estd\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003estruct\u003c/span\u003e \u003cspan class=\"nc\"\u003eListNode\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eListNode\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003enext\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003estruct\u003c/span\u003e \u003cspan class=\"nc\"\u003eTreeNode\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eleft\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eright\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003eprocessValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// TODO: 业务处理\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003etraverseArray\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003econst\u003c/span\u003e \u003cspan class=\"n\"\u003evector\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u0026amp;\u003c/span\u003e \u003cspan class=\"n\"\u003enums\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"n\"\u003enums\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e \u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eprocessValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enums\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003etraverseList\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eListNode\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eListNode\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ehead\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"k\"\u003enullptr\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003enext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eprocessValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003etraverseTree\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTreeNode\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eroot\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroot\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"k\"\u003enullptr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 前序位置\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eprocessValue\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroot\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eval\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etraverseTree\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroot\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eleft\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etraverseTree\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eroot\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e\u003cspan class=\"n\"\u003eright\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e// 后序位置\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"2-二分查找缩小搜索空间\"\u003e2) 二分查找（缩小搜索空间）\u003c/h2\u003e\n\u003ch3 id=\"解释-1\"\u003e解释\u003c/h3\u003e\n\u003cp\u003e当问题满足单调性（答案在一边）时，用二分不断折半区间。关键在区间定义一致（闭区间或左闭右开）和边界收缩逻辑一致。\u003c/p\u003e","title":"常见算法套路"},{"content":"基础防抖 1 2 3 4 5 6 7 8 9 10 11 12 const _debounce = (fn, delay, immediate = false) =\u0026gt; { let timer; return (...args) =\u0026gt; { const callNow = immediate \u0026amp;\u0026amp; !timer; clearTimeout(timer); timer = setTimeout(() =\u0026gt; { timer = null; if (!immediate) fn(...args); }, delay); if (callNow) fn(...args); }; }; 带 Promise 的防抖 鼠标拖拽时如果频繁触发防抖，而被包装函数会调 API 并返回 Promise，通常只希望最后一次真正执行。Lodash 的 debounce 要求 func 必须是函数：\n1 2 3 if (typeof func !== \u0026#39;function\u0026#39;) { throw new TypeError(\u0026#39;Expected a function\u0026#39;); } 异步场景需要稍作改造：\n只保留最后一次 Promise 1 2 3 4 5 6 7 8 9 const debounce_promise_last = (fn, delay) =\u0026gt; { let timer = null; return (...args) =\u0026gt; { clearTimeout(timer); return new Promise((resolve) =\u0026gt; { timer = setTimeout(() =\u0026gt; resolve(fn(...args)), delay); }); }; }; 每次调用都返回 Promise，延迟后统一结算 1 2 3 4 5 6 7 8 9 10 11 12 13 function debounce_promise_all(fn, delay = 0) { let timer = null; let resolves = []; return function (...args) { clearTimeout(timer); timer = setTimeout(() =\u0026gt; { const result = fn(...args); resolves.forEach((r) =\u0026gt; r(result)); resolves = []; }, delay); return new Promise((r) =\u0026gt; resolves.push(r)); }; } API 场景：节流、取消、重试 调 API 时除了防抖，节流也很常见。更复杂的情况还有取消和重试（常配合指数退避）。当 Promise 数量极大（我们曾遇到 1000+）时，通常需要：\n分批执行，例如每批 20 个； 为每个 Promise 分配唯一 ID； 做好日志（我们曾打到 Splunk）； 明确取消语义； Ajax 重试配合指数退避。 具体实现与业务强相关，这里不贴完整代码。\n","permalink":"https://yy-tech.online/zh/post/debounce-with-promise/","summary":"\u003ch2 id=\"基础防抖\"\u003e基础防抖\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003e_debounce\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003edelay\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eimmediate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e(...\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ecallNow\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eimmediate\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eclearTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003esetTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(()\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"nx\"\u003eimmediate\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"p\"\u003e(...\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"nx\"\u003edelay\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ecallNow\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"p\"\u003e(...\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"带-promise-的防抖\"\u003e带 Promise 的防抖\u003c/h2\u003e\n\u003cp\u003e鼠标拖拽时如果频繁触发防抖，而被包装函数会调 API 并返回 \u003cstrong\u003ePromise\u003c/strong\u003e，通常只希望\u003cstrong\u003e最后一次\u003c/strong\u003e真正执行。Lodash 的 \u003ccode\u003edebounce\u003c/code\u003e 要求 \u003ccode\u003efunc\u003c/code\u003e 必须是函数：\u003c/p\u003e","title":"[Autodesk] 用 Promise 做防抖"},{"content":"基础防抖 1 2 3 4 5 6 7 8 9 10 11 12 const _debounce = (fn, delay, immediate = false) =\u0026gt; { let timer; return (...args) =\u0026gt; { const callNow = immediate \u0026amp;\u0026amp; !timer; clearTimeout(timer); timer = setTimeout(() =\u0026gt; { timer = null; if (!immediate) fn(...args); }, delay); if (callNow) fn(...args); }; }; 带 Promise 的防抖 拖拽鼠标时若频繁触发防抖，而被包装函数会请求 API 并返回 Promise，往往只希望最后一次调用生效。Lodash 的 debounce 会校验 func 必须是函数：\n1 2 3 if (typeof func !== \u0026#39;function\u0026#39;) { throw new TypeError(\u0026#39;Expected a function\u0026#39;); } 异步场景可以这样写：\n只保留最后一次 Promise 1 2 3 4 5 6 7 8 9 10 11 12 const debounce_promise_last = (fn, delay) =\u0026gt; { let timer = null; return (...args) =\u0026gt; { clearTimeout(timer); return new Promise((resolve) =\u0026gt; { timer = setTimeout( () =\u0026gt; resolve(fn(...args)), delay, ); }); }; }; 每次调用都返回 Promise，延迟后一起 resolve 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function debounce_promise_all(fn, delay = 0) { let timer = null; let resolves = []; return function (...args) { clearTimeout(timer); timer = setTimeout(() =\u0026gt; { const result = fn(...args); resolves.forEach((r) =\u0026gt; r(result)); resolves = []; }, delay); return new Promise((r) =\u0026gt; resolves.push(r)); }; } API 调用：节流、取消、重试 调 API 时常用节流限制单位时间内的请求次数。更复杂的情况包括取消和重试（指数退避）。当并发 Promise 非常多（例如 1000+）时，通常需要：\n分批处理（如每批 20 个）； 为每个 Promise 绑定任务 ID； 单条日志便于排查（我们使用 Splunk）； 设计取消策略； Ajax 重试使用指数退避。 实现与产品逻辑耦合较紧，此处不展开示例代码。\n","permalink":"https://yy-tech.online/zh/post/frontend-debounce-with-promise/","summary":"\u003ch2 id=\"基础防抖\"\u003e基础防抖\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003e_debounce\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003edelay\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eimmediate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e(...\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ecallNow\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eimmediate\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003eclearTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etimer\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003esetTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(()\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nx\"\u003etimer\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"nx\"\u003eimmediate\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"p\"\u003e(...\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"nx\"\u003edelay\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ecallNow\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"p\"\u003e(...\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"带-promise-的防抖\"\u003e带 Promise 的防抖\u003c/h2\u003e\n\u003cp\u003e拖拽鼠标时若频繁触发防抖，而被包装函数会请求 API 并返回 \u003cstrong\u003ePromise\u003c/strong\u003e，往往只希望\u003cstrong\u003e最后一次\u003c/strong\u003e调用生效。Lodash 的 \u003ccode\u003edebounce\u003c/code\u003e 会校验 \u003ccode\u003efunc\u003c/code\u003e 必须是函数：\u003c/p\u003e","title":"[Autodesk] 用 Promise 做防抖"},{"content":" 接上篇，继续记录 JWT、权限与工程化细节。\n1. JWT 流程 Access Token 短有效期，提高安全性。 流程 登录换取 access token + refresh token（refresh 放 HttpOnly、Secure、SameSite Cookie）。 请求携带 payload + access token；access token 放内存，登出即清。 access 过期后用 refresh Cookie 换新 access。 access 常设约 1 分钟，缩小被盗用窗口。 参数加单向哈希做完整性校验，最好带请求时间。 敏感头字段用 HMAC 加密；刷新 token 时服务端同样校验 HMAC。 JWT 可存 Redis 做吊销；我们对外提供无状态 API，未在服务端持久化会话。 实现注意 Set-Cookie 由服务端设置，不要在前端 JS 里写 Cookie。 Cookie 必须 HttpOnly、Secure、SameSite，否则后续请求可能带不上。 取舍 refresh Cookie 更安全，但浏览器扩展仍可能读到 Cookie。无状态是为方便其他系统调用，属于有意取舍。\n2. 权限模型 3. 前端实现 redux-thunk、Redux、React、react-hooks、Konva 做单向数据流。 redux-thunk 比 redux-saga 上手快，适合多数异步。 hooks 比 class 生命周期少写样板代码。 Konva 配合 TypeScript 画 canvas 很顺手。 4. 数据库与 XSS 优先 SQLAlchemy ORM；若必须原生 SQL，务必转义与校验，防注入/XSS。 按需加索引。 重要 SQL 用 EXPLAIN 分析。 5. Flask 后端基础 日志（按天滚动 + 双 Handler） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 self.logger.setLevel(logging.DEBUG) formatter = logging.Formatter(getattr(config, \u0026#34;LOG_FORMAT\u0026#34;)) timedRotatingFileHandler = handlers.TimedRotatingFileHandler( getattr(config, \u0026#34;LOG_FILENAME\u0026#34;), when=getattr(config, \u0026#34;LOG_WHEN\u0026#34;), interval=getattr(config, \u0026#34;LOG_INTERVAL\u0026#34;), backupCount=getattr(config, \u0026#34;LOG_BACKUP_COUNT\u0026#34;), ) timedRotatingFileHandler.setLevel(logging.INFO) timedRotatingFileHandler.setFormatter(formatter) errorLogHandler = handlers.RotatingFileHandler( getattr(config, \u0026#34;LOG_ROTATE_FILENAME\u0026#34;), maxBytes=getattr(config, \u0026#34;LOG_ROTATE_MAXBYTES\u0026#34;), backupCount=getattr(config, \u0026#34;LOG_ROTATE_BACKUP_COUNT\u0026#34;), ) errorLogHandler.setLevel(logging.ERROR) errorLogHandler.setFormatter(formatter) 其他基线 分环境配置； CORS 响应头与显式 OPTIONS（可不依赖 flask-cors）； 序列化 ORM 行： 1 2 3 4 5 6 7 8 9 from sqlalchemy.inspection import inspect class Serializer(object): def serialize(self): return {c: getattr(self, c) for c in inspect(self).attrs.keys()} @staticmethod def serialize_list(lo): return [m.serialize() for m in lo] 单例（配置、日志等） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import threading lock = threading.Lock() class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: with lock: if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__( *args, **kwargs ) return cls._instances[cls] Sphinx + Postman 文档由 Markdown 生成； Postman 集合与文档放一起，团队可直接导入联调。 ","permalink":"https://yy-tech.online/zh/post/flask-with-mysql-2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e接上篇，继续记录 JWT、权限与工程化细节。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1-jwt-流程\"\u003e1. JWT 流程\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eAccess Token 短有效期\u003c/strong\u003e，提高安全性。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"流程\"\u003e流程\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e登录换取 \u003cstrong\u003eaccess token + refresh token\u003c/strong\u003e（refresh 放 HttpOnly、Secure、SameSite Cookie）。\u003c/li\u003e\n\u003cli\u003e请求携带 \u003cstrong\u003epayload + access token\u003c/strong\u003e；access token 放内存，登出即清。\u003c/li\u003e\n\u003cli\u003eaccess 过期后用 refresh Cookie 换新 access。\u003c/li\u003e\n\u003cli\u003eaccess 常设约 1 分钟，缩小被盗用窗口。\u003c/li\u003e\n\u003cli\u003e参数加\u003cstrong\u003e单向哈希\u003c/strong\u003e做完整性校验，最好带请求时间。\u003c/li\u003e\n\u003cli\u003e敏感头字段用 \u003cstrong\u003eHMAC\u003c/strong\u003e 加密；刷新 token 时服务端同样校验 HMAC。\u003c/li\u003e\n\u003cli\u003eJWT 可存 \u003cstrong\u003eRedis\u003c/strong\u003e 做吊销；我们对外提供无状态 API，未在服务端持久化会话。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"实现注意\"\u003e实现注意\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eSet-Cookie\u003c/code\u003e 由\u003cstrong\u003e服务端\u003c/strong\u003e设置，不要在前端 JS 里写 Cookie。\u003c/li\u003e\n\u003cli\u003eCookie 必须 \u003ccode\u003eHttpOnly\u003c/code\u003e、\u003ccode\u003eSecure\u003c/code\u003e、\u003ccode\u003eSameSite\u003c/code\u003e，否则后续请求可能带不上。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"取舍\"\u003e取舍\u003c/h3\u003e\n\u003cp\u003erefresh Cookie 更安全，但浏览器扩展仍可能读到 Cookie。无状态是为方便其他系统调用，属于有意取舍。\u003c/p\u003e","title":"[GLP] Flask 搭配 MySQL（2）"},{"content":" 一个 Flask 后端脚手架。之前写的 Python 多是脚本，这次想搭一套像样的后端骨架。\n1. 需要的能力 flask blueprint 拆分路由 CORS JSON 响应 方便调试 健康检查接口 日志 多环境配置 flake8 静态检查 yapf 格式化 JWT 密码哈希 MySQL Sphinx 文档 Postman 集合 测试 XSS 防护（ORM + 转义） 参数完整性校验（SHA-1，防篡改） 时间戳校验（防重放） ","permalink":"https://yy-tech.online/zh/post/flask-with-mysql-1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e一个 Flask 后端脚手架。之前写的 Python 多是脚本，这次想搭一套像样的后端骨架。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"1-需要的能力\"\u003e1. 需要的能力\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eflask\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eblueprint\u003c/code\u003e 拆分路由\u003c/li\u003e\n\u003cli\u003eCORS\u003c/li\u003e\n\u003cli\u003eJSON 响应\u003c/li\u003e\n\u003cli\u003e方便调试\u003c/li\u003e\n\u003cli\u003e健康检查接口\u003c/li\u003e\n\u003cli\u003e日志\u003c/li\u003e\n\u003cli\u003e多环境配置\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eflake8\u003c/code\u003e 静态检查\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eyapf\u003c/code\u003e 格式化\u003c/li\u003e\n\u003cli\u003eJWT\u003c/li\u003e\n\u003cli\u003e密码哈希\u003c/li\u003e\n\u003cli\u003eMySQL\u003c/li\u003e\n\u003cli\u003eSphinx 文档\u003c/li\u003e\n\u003cli\u003ePostman 集合\u003c/li\u003e\n\u003cli\u003e测试\u003c/li\u003e\n\u003cli\u003eXSS 防护（ORM + 转义）\u003c/li\u003e\n\u003cli\u003e参数完整性校验（SHA-1，防篡改）\u003c/li\u003e\n\u003cli\u003e时间戳校验（防重放）\u003c/li\u003e\n\u003c/ul\u003e","title":"[GLP] Flask 搭配 MySQL（1）"},{"content":" 今天一个同事甩我一段代码，让我实现功能，说是对图片做半透明太慢了 我拿到手头一看，代码是这样的，据说是一个别的组算法厉害的大神写的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # -*- coding: utf-8 -*- import numpy as np import os import cv2 def put_mask(img_path,output_fold): image = cv2.imread(r\u0026#39;E:\\testdemo.jpg\u0026#39;) bbox1 = [72,41,208,330] bbox2 = [100,80,248,334] zeros1 = np.zeros((image.shape), dtype=np.uint8) zeros2 = np.zeros((image.shape), dtype=np.uint8) zeros_mask1 = cv2.rectangle(zeros1, (bbox1[0], bbox1[1]), (bbox1[2], bbox1[3]),color=(0,0,255), thickness=-1 ) zeros_mask2 = cv2.rectangle(zeros2, (bbox2[0], bbox2[1]), (bbox2[2], bbox2[3]),color=(0, 255, 0), thickness=-1) zeros_mask = np.array((zeros_mask1 + zeros_mask2)) try: # alpha 为第一张图片的透明度 alpha = 1 # beta 为第二张图片的透明度 beta = 0.5 gamma = 0 # cv2.addWeighted 将原始图片与 mask 融合 mask_img = cv2.addWeighted(image, alpha, zeros_mask, beta, gamma) cv2.imwrite(os.path.join(output_fold,\u0026#39;mask_img.jpg\u0026#39;), mask_img) except: print(\u0026#39;异常\u0026#39;) put_mask(img_path = \u0026#39;107.jpg\u0026#39;, output_fold=\u0026#39;E:\\output\u0026#39;) 我看了之后只是觉得，这代码写的太草率了吧，大家都说python简单，没错，是简单，但是也不能乱写吧。这一看就是把mask层放大到和图片一样大，再做addWeighted，addweight本来就慢，还变成大的来做，就不能只对小的部分做完再拼回去么，于是我写了一下测试代码如下，这甚至都谈不上什么算法，只是工程上的直觉而已，一个个像素处理，自然是能少处理就少处理一些，也才能块一些呗 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # -*- coding: utf-8 -*- import cv def combine_two_color_images(image1, image2): masklayer, background = image1.copy(), image2.copy() masklayer_height = masklayer.shape[0] masklayer_width = masklayer.shape[1] alpha =0.5 # do composite on the upper-left corner of the background image. blended_portion = cv.addWeighted(masklayer, alpha, background[:masklayer_height,:masklayer_width,:], 1 - alpha, 0, background) background[:masklayer_height,:masklayer_width,:] = blended_portion cv.imshow(\u0026#39;composited image\u0026#39;, background) cv.waitKey(10000) 结果测下来，快了非常多，以前慢的感觉完全不存在了，主要原因，我们的mask层，一般只有200 * 200，但是图片大小是1960 * 1080，这个处理级别一下就差了50倍左右，然后我们一般每小时要处理上万张图片的数量级，所以这个差距就差很多了。写代码还是要把效率放心上才是。 吐槽完毕。 ","permalink":"https://yy-tech.online/zh/post/why-is-it-running-slow/","summary":"\u003cul\u003e\n\u003cli\u003e今天一个同事甩我一段代码，让我实现功能，说是对图片做半透明太慢了\u003c/li\u003e\n\u003cli\u003e我拿到手头一看，代码是这样的，据说是一个别的组算法厉害的大神写的\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# -*- coding: utf-8 -*-\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003enumpy\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003enp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecv2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eput_mask\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eimg_path\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"n\"\u003eoutput_fold\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eimage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003er\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;E:\\testdemo.jpg\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebbox1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e72\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e41\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e208\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e330\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebbox2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e80\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e248\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e334\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ezeros1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ezeros\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003edtype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003euint8\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ezeros2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ezeros\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003edtype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003euint8\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ezeros_mask1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erectangle\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezeros1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebbox1\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ebbox1\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebbox1\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ebbox1\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\u003cspan class=\"n\"\u003ecolor\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e255\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003ethickness\u003c/span\u003e\u003cspan class=\"o\"\u003e=-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ezeros_mask2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erectangle\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezeros2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebbox2\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ebbox2\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebbox2\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"n\"\u003ebbox2\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e]),\u003c/span\u003e\u003cspan class=\"n\"\u003ecolor\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e255\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003ethickness\u003c/span\u003e\u003cspan class=\"o\"\u003e=-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ezeros_mask\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003earray\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"n\"\u003ezeros_mask1\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003ezeros_mask2\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"c1\"\u003e# alpha 为第一张图片的透明度\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ealpha\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# beta 为第二张图片的透明度\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ebeta\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mf\"\u003e0.5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003egamma\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# cv2.addWeighted 将原始图片与 mask 融合\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003emask_img\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eaddWeighted\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ealpha\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ezeros_mask\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebeta\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003egamma\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimwrite\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eoutput_fold\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;mask_img.jpg\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003emask_img\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;异常\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eput_mask\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eimg_path\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;107.jpg\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eoutput_fold\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;E:\\output\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e我看了之后只是觉得，这代码写的太草率了吧，大家都说python简单，没错，是简单，但是也不能乱写吧。这一看就是把mask层放大到和图片一样大，再做addWeighted，addweight本来就慢，还变成大的来做，就不能只对小的部分做完再拼回去么，于是我写了一下测试代码如下，这甚至都谈不上什么算法，只是工程上的直觉而已，一个个像素处理，自然是能少处理就少处理一些，也才能块一些呗\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# -*- coding: utf-8 -*-\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecv\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ecombine_two_color_images\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eimage1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage2\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emasklayer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebackground\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eimage1\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecopy\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003eimage2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecopy\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emasklayer_height\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emasklayer\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emasklayer_width\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003emasklayer\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ealpha\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mf\"\u003e0.5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# do composite on the upper-left corner of the background image.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eblended_portion\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eaddWeighted\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emasklayer\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ealpha\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ebackground\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"n\"\u003emasklayer_height\u003c/span\u003e\u003cspan class=\"p\"\u003e,:\u003c/span\u003e\u003cspan class=\"n\"\u003emasklayer_width\u003c/span\u003e\u003cspan class=\"p\"\u003e,:],\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"n\"\u003ealpha\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ebackground\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ebackground\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"n\"\u003emasklayer_height\u003c/span\u003e\u003cspan class=\"p\"\u003e,:\u003c/span\u003e\u003cspan class=\"n\"\u003emasklayer_width\u003c/span\u003e\u003cspan class=\"p\"\u003e,:]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eblended_portion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ecv\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimshow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;composited image\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebackground\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ecv\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewaitKey\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e10000\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e结果测下来，快了非常多，以前慢的感觉完全不存在了，主要原因，我们的mask层，一般只有200 * 200，但是图片大小是1960 * 1080，这个处理级别一下就差了50倍左右，然后我们一般每小时要处理上万张图片的数量级，所以这个差距就差很多了。写代码还是要把效率放心上才是。\u003c/li\u003e\n\u003cli\u003e吐槽完毕。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"[GLP] Why it runing slow?"},{"content":"BloomFilter概念 布隆过滤器的原理是，当一个元素被加入集合时，通过K个散列函数将这个元素映射成一个位数组中的K个点，把它们置为1。检索时，我们只要看看这些点是不是都是1就（大约）知道集合中有没有它了：如果这些点有任何一个0，则被检元素一定不在；如果都是1，则被检元素很可能在。这就是布隆过滤器的基本思想。 Bloom Filter跟单哈希函数Bit-Map不同之处在于：Bloom Filter使用了k个哈希函数，每个字符串跟k个bit对应。从而降低了冲突的概率。\n缓存穿透 意思是我写一个不存在的ID，一直去访问redis，没命中的话，就会都落到数据库，为了避免这种问题，需要先判断下元素在不在，不在的话，直接返回，降低数据库的压力。\nbloomfilter的不足 bloom filter之所以能做到在时间和空间上的效率比较高，是因为牺牲了判断的准确率、删除的便利性\n存在误判，可能要查到的元素并没有在容器中，但是hash之后得到的k个位置上值都是1。如果bloom filter中存储的是黑名单，那么可以通过建立一个白名单来存储可能会误判的元素。 删除困难。一个放入容器的元素映射到bit数组的k个位置上是1，删除的时候不能简单的直接置为0，可能会影响其他元素的判断。可以采用Counting Bloom Filter 我在项目是怎么做的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 const hashers = (value, times) =\u0026gt; { const hash = crypto.createHash(\u0026#39;sha1\u0026#39;).update(value).digest(); const h1 = hash.readUInt32BE(8); const h2 = hash.readUInt32BE(12); const hashes = []; //only two hash functions are necessary to effectively implement a Bloom filter without //any loss in the asymptotic false positive probability for (let i = 1; i \u0026lt;= times; i++) { hashes.push(h1 + h2 * i); } return hashes; }; class BloomFilter { constructor({ cache, size = 1000000, errorRate = 0.005 }) { if (cache) { const [mark, m, k, e, bits] = cache; this._m = m; this._k = k; this._e = e; this._bits = new Bits(0, bits); } else { this._m = Math.round((-1 * size * Math.log(errorRate)) / LN2_SQUARE); if (this._m % BYTE_LEN) { this._m += (BYTE_LEN - (this._m % BYTE_LEN)); } this._k = Math.max(1, Math.round(this._m / size * Math.LN2)); this._e = errorRate; this._bits = new Bits(); } } add(value) { if (isInteger(value)) { this._bits.set(value); } else { hashers(value, this._k).forEach(_ =\u0026gt; this._bits.set(_ % this._m)); } return this; } has(value) { if (isInteger(value)) { return this._bits.get(value); } return hashers(value, this._k).every(_ =\u0026gt; this._bits.get(_ % this._m)); } } module.exports = BloomFilter; 总结 从实现上说，guava也有对应实现了，这里是用node写的，这里的hash函数是参照下面参考文件来设置\n【1】 https://www.eecs.harvard.edu/~michaelm/postscripts/rsa2008.pdf\n","permalink":"https://yy-tech.online/zh/post/play-with-bloomfilter/","summary":"\u003ch1 id=\"bloomfilter概念\"\u003eBloomFilter概念\u003c/h1\u003e\n\u003cp\u003e布隆过滤器的原理是，当一个元素被加入集合时，通过K个散列函数将这个元素映射成一个位数组中的K个点，把它们置为1。检索时，我们只要看看这些点是不是都是1就（大约）知道集合中有没有它了：如果这些点有任何一个0，则被检元素一定不在；如果都是1，则被检元素很可能在。这就是布隆过滤器的基本思想。\nBloom Filter跟单哈希函数Bit-Map不同之处在于：Bloom Filter使用了k个哈希函数，每个字符串跟k个bit对应。从而降低了冲突的概率。\u003c/p\u003e","title":"[Autodesk] Play With bloomfilter"},{"content":"shell逐行处理两种常用速度较快的方法 使用文件描述符，把标准输出关联到文件描述符4上面，然后重定向标准输出到$OUTFILE,然后回复标准输出并且关闭文件描述符4\n1 2 3 4 5 6 7 8 9 10 11 12 13 function while_read_line_bottom_fd_out { \u0026gt;$OUTFILE exec 4\u0026lt;\u0026amp;1 exec 1\u0026gt; $OUTFILE while read LINE do echo \u0026#34;$LINE\u0026#34; : done \u0026lt; $INFILE exec 1\u0026lt;$4 exec 4\u0026gt;\u0026amp;- } 不使用文件描述符的版本,这个处理起来比较方便，容易维护\n1 2 3 4 5 6 7 8 9 function while_read_line_bottom { \u0026gt;$OUTFILE while read LINE do echo \u0026#34;$LINE\u0026#34; \u0026gt;\u0026gt; $OUTFILE : done \u0026lt; $INFILE } 简单的部署任务\n任务描述，拷贝文件夹到远程目录 一般来说一句命令就行了scp /home/frank/local.txt root@192.168.1.100:/home/frank/ 但是我们想写个shell脚本，自动拷贝目录到15台远程机器上 目标：不进行密码提示，直接拷贝，对每个机器的配置不同，要先sed处理配置文件，以及expect使用 因为我是mac机器做这个操作，所以直接本地安装expect, brew install expect安装成功后如下： 1 2 3 4 5 6 7 🍺 /usr/local/Cellar/tcl-tk/8.6.11_1: 3,041 files, 51.6MB ==\u0026gt; Installing expect ==\u0026gt; Pouring expect--5.45.4_1.big_sur.bottle.tar.gz License: Public Domain ==\u0026gt; Dependencies Build: autoconf ✔, automake ✔, libtool ✘ Required: tcl-tk ✔ 先把要部署上去的服务器的信息写好到配置文件中，但是我有一堆按行的配置信息，拷贝过来就是这样，所以要先处理下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 园区1 100001 user1 pass1 类型1 类型2 园区2 100002 user2 pass 抽帧服务器 room/road ..以下还有50条 emacs宏命令处理这种就挺方便的 1 2 3 4 5 6 7 8 9 10 11 12 13 园区1,100001,user1,pass1,类型1,类型2 园区2,100002,user2,pass,类型3,类型4 ..以下还有50条 # setting emacs macros as below. # F3 to start define macro # Ctrl-e to go to the end of the line # then add space and dot # the second space will to to next line # the delete will make second line append to the end of first line # F4 to end define macro # go to certain line, and Enjoy F4 to help you avoid the dirty operation. F3 + Ctrl-e + space + , + space + delete +F4 拷贝脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #!/bin/sh # add time for user friendly output curtime=$(date +%Y-%m-%d,%H:%M:%S) # read server config from file cat /Users/frank/server.yaml | while read line do IFS=\u0026#39;, \u0026#39; read -r -a serverConfig \u0026lt;\u0026lt;\u0026lt; $line echo [$curtime] \u0026#34;Start To Deploy: ${serverConfig[5]}\u0026#34; # 1. read APICONST file and replace with server config and split by comma # 2. replace base url and instrument url BASE_URL=\u0026#34;${serverConfig[1]}:${serverConfig[8]}\u0026#34; INSTRUMENT_URL=\u0026#34;${serverConfig[1]}:${serverConfig[9]}\u0026#34; # below command will work in mac environment, if you have linux, try this one instead # sed -i \u0026#39;s/BASE_URL:.*,/BASE_URL: \u0026#39;\\\u0026#34;${BASE_URL}\\\u0026#34;,\u0026#39;/g\u0026#39; /Users/g2/work/fe-algo/src/services/APIConst.js sed -i \u0026#34;\u0026#34; \u0026#39;s/BASE_URL:.*,/BASE_URL: \u0026#39;\\\u0026#34;${BASE_URL}\\\u0026#34;,\u0026#39;/g\u0026#39; src/services/APIConst.js sed -i \u0026#34;\u0026#34; \u0026#39;s/INSTURMENT_URL:.*,/INSTURMENT_URL: \u0026#39;\\\u0026#34;${INSTRUMENT_URL}\\\u0026#34;,\u0026#39;/g\u0026#39; src/services/APIConst.js echo [$curtime] \u0026#34;Deploy ${serverConfig[5]} : [......Base URL] change to: ${BASE_URL}\u0026#34; echo [$curtime] \u0026#34;Deploy ${serverConfig[5]} : [Instrument URL] change to: ${INSTRUMENT_URL}\u0026#34; # 3. npm run build to build website echo [$curtime] \u0026#34;Working Directory: ${PWD}\u0026#34; echo [$curtime] \u0026#34;Check Node_Module Directory...\u0026#34; NodeModuleDir=\u0026#34;${PWD}/node_modules\u0026#34; if [ -d \u0026#34;$NodeModuleDir\u0026#34; ]; then echo [$curtime] \u0026#34;node module already existed, run npm build directly\u0026#34; else echo [$curtime] \u0026#34;no node module found, run npm install and then build\u0026#34; npm install \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 fi echo [$curtime] \u0026#34;Start To Build: \u0026#34; # npm run build \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 curtime=$(date +%Y-%m-%d,%H:%M:%S) echo [$curtime] \u0026#34;Build Completed!\u0026#34; echo [$curtime] \u0026#34;Check Build Output:\u0026#34; ls -la ${PWD}/dist/ # 4. scp copy current dist folder to certain server set timeout 160 username=${serverConfig[3]} host=${serverConfig[1]} pass=${serverConfig[4]} port=${serverConfig[2]} echo \u0026#34;username:${username},host:${host},pass:${pass},port:${port}\u0026#34; echo \u0026#34;scp command:\u0026#34; echo \u0026#34;scp -P ${port} -r ${PWD}/dist/ ${username}@${host}:/home/${username}/work/webcontent/\u0026#34; expect_commands=\u0026#34; spawn scp -P ${port} -r ${PWD}/dist ${username}@${host}:/home/${username}/work/webcontent expect \\\u0026#34;password:\\\u0026#34; send \\\u0026#34;${pass}\\r\\\u0026#34; expect eof \u0026#34; expect -c \u0026#34;${expect_commands}\u0026#34; curtime=$(date +%Y-%m-%d,%H:%M:%S) echo [$curtime] \u0026#34;SCP Copy file Completed.\u0026#34; echo \u0026#34;[$curtime] scp completed.\u0026#34; # 5. check server status: nginx, frp # 6. output the machine deploy status done # add progressing bar when doing the deployment ","permalink":"https://yy-tech.online/zh/post/play-with-shell/","summary":"\u003ch2 id=\"shell逐行处理两种常用速度较快的方法\"\u003eshell逐行处理两种常用速度较快的方法\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e使用文件描述符，把标准输出关联到文件描述符4上面，然后重定向标准输出到\u003ccode\u003e$OUTFILE\u003c/code\u003e,然后回复标准输出并且关闭文件描述符4\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efunction\u003c/span\u003e while_read_line_bottom_fd_out\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u0026gt;\u003cspan class=\"nv\"\u003e$OUTFILE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexec\u003c/span\u003e 4\u0026lt;\u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexec\u003c/span\u003e 1\u0026gt; \u003cspan class=\"nv\"\u003e$OUTFILE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"nb\"\u003eread\u003c/span\u003e LINE\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$LINE\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    :\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edone\u003c/span\u003e \u0026lt; \u003cspan class=\"nv\"\u003e$INFILE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexec\u003c/span\u003e 1\u0026lt;\u003cspan class=\"nv\"\u003e$4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexec\u003c/span\u003e 4\u0026gt;\u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e-\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e不使用文件描述符的版本,这个处理起来比较方便，容易维护\u003c/p\u003e","title":"[Autodesk] Play With Shell"},{"content":"动机 公司内网想做一个小型的https，不想申请https证书，想自己做自验证的证书\n关注点 https的握手流程\n浏览器尝试连接网站 https://demowebsite.com. demowebsite.com server 会把证书送回给浏览器。这个证书包含网站服务器的公钥，还有一些其他信息证明这个公钥属于网站 浏览器验证这个证书来确认他有正确的公钥 浏览器选择一个随机对称key K用来连接到服务器。客户端使用公钥加密K 服务端使用私钥解密K,这样客户端服务器都知道K,但是其他人不知道 接着任何从客户端发送到服务端的信息，都用K加密。 自己做CA\n要先创建root CA, 这个root ca不做客户端或者服务器端证书的签发，它是用来创建一个中间CA,这个中间CA会替代root CA签发证书，这样root key就可以离线保存，这样安全性才能得到保证 准备目录结构 创建目录,命令和最后结果类似如下 1 2 3 4 5 6 7 mkdir -p /root/ca cd /root/ca mkdir certs crl newcerts private chmod 700 private touch index.txt touch serial echo 1000 \u0026gt; serial 准备配置文件openssl.cnf 创建root key 1 2 3 cd /root/ca openssl genrsa -aes256 -out private/ca.key.pem 4096 chmod 400 private/ca.key.pem 创建root secret 1 2 3 4 5 6 cd /root/ca openssl req -config openssl.cnf \\ -key private/ca.key.pem \\ -new -x509 -days 7300 -sha256 -extensions v3_ca \\ -out certs/ca.cert.pem chmod 444 certs/ca.cert.pem 验证root证书 1 openssl x509 -noout -text -in certs/ca.cert.pem 创建中间证书颁发机构\n创建目录 1 2 3 4 5 6 7 8 mkdir /root/ca/intermediate cd /root/ca/intermediate mkdir certs crl csr newcerts private chmod 700 private touch index.txt touch serial touch crlnumber echo 1000 \u0026gt; serial 准备配置文件openssl-intermediate.cnf 创建intermediate key 1 2 3 4 cd /root/ca openssl req -config intermediate/openssl-intermediate.cnf -new -sha256 \\ -key intermediate/private/intermediate.key.pem \\ -out intermediate/csr/intermediate.csr.pem 创建intermediate secret 1 2 3 4 5 6 cd /root/ca openssl ca -config openssl.cnf -extensions v3_intermediate_ca \\ -days 3650 -notext -md sha256 \\ -in intermediate/csr/intermediate.csr.pem \\ -out intermediate/certs/intermediate.cert.pem chmod 444 intermediate/certs/intermediate.cert.pem 验证证书 1 2 3 4 5 6 // 和上面一样验证中间证书的有效性 openssl x509 -noout -text \\ -in intermediate/certs/intermediate.cert.pem // 使用根证书验证中间证书 openssl verify -CAfile certs/ca.cert.pem \\ intermediate/certs/intermediate.cert.pem 证书链的生成\n一般如果有证书通过中间证书颁发机构来验证，也要去根证书机构验证 使用如下来把根证书和中间证书合并到一起 1 2 3 cat intermediate/certs/intermediate.cert.pem \\ certs/ca.cert.pem \u0026gt; intermediate/certs/ca-chain.cert.pem chmod 444 intermediate/certs/ca-chain.cert.pem 服务器的证书部署\n签发服务端和客户端的证书，和上面一样，只不过用中间证书颁发机构的配置来创建 创建key 1 2 3 4 cd /root/ca openssl genrsa -aes256 \\ -out intermediate/private/www.example.com.key.pem 2048 chmod 400 intermediate/private/www.example.com.key.pem 创建证书签发请求 1 2 3 4 cd /root/ca openssl req -config intermediate/openssl.cnf \\ -key intermediate/private/www.example.com.key.pem \\ -new -sha256 -out intermediate/csr/www.example.com.csr.pem 创建服务端证书 1 2 3 4 5 6 cd /root/ca openssl ca -config intermediate/openssl.cnf \\ -extensions server_cert -days 375 -notext -md sha256 \\ -in intermediate/csr/www.example.com.csr.pem \\ -out intermediate/certs/www.example.com.cert.pem chmod 444 intermediate/certs/www.example.com.cert.pem 创建完后，可以在intermediate/index.txt文件中有一条相应的记录 验证证书 1 2 3 4 5 openssl x509 -noout -text \\ -in intermediate/certs/www.example.com.cert.pem # 使用证书链文件来验证新建的证书 openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \\ intermediate/certs/www.example.com.cert.pem 部署需要的证书 1 2 3 ca-chain.cert.pem www.example.com.key.pem www.example.com.cert.pem 证书验证\n1 2 3 4 // 这个命令会显示服务器的CA证书,showcerts openssl s_client -connect localhost:443 -prexit -showcerts // 验证当前的证书是不是被根ca签发 openssl verify -verbose -x509_strict -CAfile ca-chain.cert.pem localhost.cert.pem 代码里怎么用\n建立一个简单的torado web服务器 把上面三个文件拷贝到对应目录 代码如下： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /* 这里要注意ssl_options,要把证书链也加进去，不然是不行的 */ import tornado.ioloop import tornado.web import ssl class MainHandler(tornado.web.RequestHandler): def get(self): self.write(\u0026#34;Hello, world\u0026#34;) def make_app(): return tornado.web.Application([ (r\u0026#34;/\u0026#34;, MainHandler), ]) if __name__ == \u0026#34;__main__\u0026#34;: application = make_app() chainpath=\u0026#34;./ca-chain.cert.pem\u0026#34; crtpath=\u0026#34;./localhost.cert.pem\u0026#34; keypath=\u0026#34;./localhost.key.pem\u0026#34; ssl_ctx= ssl.create_default_context(ssl.Purpose.CLIENT_AUTH,cafile=chainpath) ssl_ctx.load_cert_chain(crtpath,keypath) http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_ctx) http_server.listen(443) tornado.ioloop.IOLoop.current().start() 如何测试\n浏览器上的话，可以考虑firefox,chrome也可以，不过要自己导入证书，可能还要做格式转化，下面直接在firefox里导入证书，结果如下： 如果你是用request这类的库，可以像这样做,因为我们对key是做加密的，所以要把密码也传到后端，不能直接request直接用key,cert请求，除非你生成证书的时候不加密码。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import requests from urllib3.util.ssl_ import create_urllib3_context from requests.adapters import HTTPAdapter import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) cert_path = \u0026#34;./localhost.cert.pem\u0026#34; private_key_path = \u0026#34;./localhost.key.pem\u0026#34; passphrase_key = \u0026#34;xxx\u0026#34; class SSLAdapter(HTTPAdapter): def init_poolmanager(self, *args, **kwargs): context = create_urllib3_context() context.load_cert_chain( certfile=cert_path, keyfile=private_key_path, password=passphrase_key) kwargs[\u0026#39;ssl_context\u0026#39;] = context return super().init_poolmanager(*args, **kwargs) session = requests.Session() session.verify = False # If you don\u0026#39;t want to validate server\u0026#39;s public certificate session.mount(\u0026#34;https://\u0026#34;, SSLAdapter()) url = \u0026#34;https://localhost:20191/inference\u0026#34; response = session.post(url) print(response.json()) 总结\n这里还有些其他主题，比如，证书回收，格式转换方法，测速，最佳实践等，这里主要记录下做这种证书的步骤，其他的值得一提的是，最佳实践，我们一般是做在CI/CD的pipline里边，证书也要做个定时更新，这些都是必不可少的，当然在生成证书之前还有一些对比和选择加密算法的细节，这里就不一一讲了，有兴趣可以自己去找下。 ","permalink":"https://yy-tech.online/zh/post/https-self-signed-certificate/","summary":"\u003ch2 id=\"动机\"\u003e动机\u003c/h2\u003e\n\u003cp\u003e公司内网想做一个小型的https，不想申请https证书，想自己做自验证的证书\u003c/p\u003e\n\u003ch2 id=\"关注点\"\u003e关注点\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003ehttps的握手流程\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cimg alt=\"react_redux\" loading=\"lazy\" src=\"/images/ssl.png\"\u003e\u003c/li\u003e\n\u003cli\u003e浏览器尝试连接网站 \u003ca href=\"https://demowebsite.com\"\u003ehttps://demowebsite.com\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003edemowebsite.com server 会把证书送回给浏览器。这个证书包含网站服务器的公钥，还有一些其他信息证明这个公钥属于网站\u003c/li\u003e\n\u003cli\u003e浏览器验证这个证书来确认他有正确的公钥\u003c/li\u003e\n\u003cli\u003e浏览器选择一个随机对称key K用来连接到服务器。客户端使用公钥加密K\u003c/li\u003e\n\u003cli\u003e服务端使用私钥解密K,这样客户端服务器都知道K,但是其他人不知道\u003c/li\u003e\n\u003cli\u003e接着任何从客户端发送到服务端的信息，都用K加密。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e自己做CA\u003c/p\u003e","title":"[Autodesk] How to create https self-signed certificate"},{"content":"什么是web assembly 就是一个可移植、体积小、加载快并且兼容 Web 的全新格式。可以使用C、C++、Rust、Go、Java、C#等编译器来生成wasm,以二进制的方式发送给浏览器，可以增强javascript的效率。目前的实践情况有c++开发的大型游戏，google earth, Magnum，Blazor，我司的autocad.这里主要介绍我司的autocad.\n为什么用web assembly 这个就不必说了，主要是效率问题，js显然对于效率这块有点无能为力。对于大型游戏和对于画图这种很重cpu,内存的，以及高并发的场景，javascript支持其实有限，当然也有增加这方面的能力，比如sharedArrayBuffer来提供某种程度的并发，但是其实还是相当有限。我司的使用是emScripten和asm.js然后，后面用更加优化的Binaryen,整体速度提高了大概12%-15%左右。\n我们怎么用 我们主要是用emScripten来把c++编译成wasm.主要问题在于：\n代码code base太大 应用启动时间长 桌面应用和网站的区别 同步和异步IO\n浏览器主线程不允许阻塞调用，不然就很卡了，重写可能比较麻烦，第三方库可能也需要重写 弄个web worker来做也有点问题，比如阻塞的调用，我后面onMessage接受不到数据，也没有共享内存，没有类似信号量的机制来保证我的通讯机制 缺乏共享内存\nEmterpreter: 他是可以把asm.js编译成bytecode,然后有个解释器可以来跑，可以支持同步,主要是有可以保存执行的状态和堆栈，然后有个定时器，然后恢复堆栈和状态这样的方式来支持同步。问题是执行太慢，而且没有一种确切的方式来识别栈中的函数，维护也困难。 SharedArrayBuffer:很快，但是要手动去处理序列化，更大的问题是有幽灵攻击的漏洞存在，所以也不行 service worker \u0026amp; xhr: 使用同步XHR来模拟阻塞调用，用service worker来拦截网络调用，当有新版本的service worker上线的时候，用户要刷新才能看到,这里就要自己管理版本保证一致性，所以需要做个后台更新,还有就是启动的时候需要等待service worker的启动完成，这也算是一个启动时间的一种负担 内存访问不一致性的问题 第一，asm.js直接不支持，wasm跑的很慢，主要是涉及一些强制类型转换的时候的内存拷贝，比如char的指针拷贝到int的指针的位置，所以后来统一换成了memcpy,主要是code修改，避免wasm变慢影响性能。\n1 2 3 4 5 int* a = new int; unsigned char b = 1; a = (int*) \u0026amp;b; /*改成*/ memcpy(a,\u0026amp;b,sizeof(int)) 第二是：函数指针的转换，emscripten的函数指针需要严格类型\n1 2 3 typedef void (*voidType)(int); int myfun(int a){} voidType fn = (voidType)myfun; //这里报错 优化后82个子项目提升了50%左右，构建从90分钟降低到50分钟\n不支持异常：移除部分异常，提高效率\n总结 启动过程，从UI主线程开始，首先是UI初始化，service worker初始化，初始化web worker,web worker里边又包括wasm初始化，下载资源（font, 国际化的数据等等），然后是c++初始化启动的代码。 启动优化：因为web worker中的wasm实例化占了大部分的时间，所以web assembly实例化的速度可以优化，还有就是代码优化比如O3(采取很多向量化算法，提高代码的并行执行程度，利用现代CPU中的流水线，Cache等),降低code size移除异常等等 ","permalink":"https://yy-tech.online/zh/post/webassembly-autodesk/","summary":"\u003ch2 id=\"什么是web-assembly\"\u003e什么是web assembly\u003c/h2\u003e\n\u003cp\u003e就是一个可移植、体积小、加载快并且兼容 Web 的全新格式。可以使用C、C++、Rust、Go、Java、C#等编译器来生成wasm,以二进制的方式发送给浏览器，可以增强javascript的效率。目前的实践情况有c++开发的大型游戏，google earth, Magnum，Blazor，我司的autocad.这里主要介绍我司的autocad.\u003c/p\u003e","title":"[Autodesk] how autodesk use webassembly "},{"content":"PPT 组内分享的PPT conttainer namespace linux control group UnionFS veth 容器应该具备哪些东西 隔离文件系统： 通过 chroot 命令切换根目录的挂载点 隔离网络： 为了分布式环境下的通讯： 要有独立的 IP、端口和路由 veth, 每个容器用有其独立的网络设备，IP 地址，IP 路由表，/proc/net 目录，端口号等等。这也使得一个 host 上多个容器内的同一个应用都绑定到各自容器的 80 端口上 主机名：需要一个主机名方便在网络中标识自己 IPC: 每个容器有其自己的 System V IPC 和 POSIX 消息队列文件系统，因此，只有在同一个 IPC namespace 的进程之间才能互相通信 用户权限: 在 user namespace 中的进程的用户和组 ID 可以和在 host 上不同； 每个 container 可以有不同的 user 和 group id；一个 host 上的非特权用户可以成为 user namespace 中的特权用户 容器主要的部分 namespace UTS, IPC, PID, NETWORK, MOUNT, USER cgroup CPU, Memory, Blkio, Device\u0026hellip; UnionFS aufs(ubuntu), btrfs(suse), vfs, devicemapper(centos), overlayer2(centos,ubuntu) veth bridge, host, container, none 容器实现 容器是特殊的进程 int clone(int (*fn)(void*), void *child_stack, int flags, void*arg); Fork = Clone + CLONE_VM | CLONE_VFORK | SIGHILD (NOTE: Fork和Clone都是对sys_clone的封装，所以其实这里表达有少许不准确) fork是从调用点继续执行，clone是从fn(args)继续执行，因为子进程和父进程共享内存，但是维护单独的变量副本，所以需要为子进程单独分配栈，就是child_stack指针指向的位置，flags有两个作用，低位字节可以放返回信号，flags和docker相关的flag就是上述的UTS, IPC, PID, NETWORK, MOUNT, USER对应的标签CLONE_NEWUTS,CLONE_NEWIPC,CLONE_NEWPID,CLONE_NEWNET,CLONE_NEWNS,CLONE_NEWUSER cgroup包含着对资源的控制，对应linux下是在/sys/fs/cgroup目录下 UnionFS对应overlay2FS的路径是sudo ls /var/lib/docker/image/overlay2/layerdb/${id} veth:虚拟网卡 ","permalink":"https://yy-tech.online/zh/post/docker-container-study/","summary":"\u003ch2 id=\"ppt\"\u003ePPT\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"/images/tom.key\"\u003e\u003cstrong\u003e组内分享的PPT\u003c/strong\u003e\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"conttainer\"\u003econttainer\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003enamespace\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elinux control group\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eUnionFS\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eveth\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cul\u003e\n\u003cli\u003e容器应该具备哪些东西\n\u003cul\u003e\n\u003cli\u003e隔离文件系统： 通过 chroot 命令切换根目录的挂载点\u003c/li\u003e\n\u003cli\u003e隔离网络： 为了分布式环境下的通讯： 要有独立的 IP、端口和路由 veth, 每个容器用有其独立的网络设备，IP 地址，IP 路由表，/proc/net 目录，端口号等等。这也使得一个 host 上多个容器内的同一个应用都绑定到各自容器的 80 端口上\u003c/li\u003e\n\u003cli\u003e主机名：需要一个主机名方便在网络中标识自己\u003c/li\u003e\n\u003cli\u003eIPC: 每个容器有其自己的 System V IPC 和 POSIX 消息队列文件系统，因此，只有在同一个 IPC namespace 的进程之间才能互相通信\u003c/li\u003e\n\u003cli\u003e用户权限: 在 user namespace 中的进程的用户和组 ID 可以和在 host 上不同； 每个 container 可以有不同的 user 和 group id；一个 host 上的非特权用户可以成为 user namespace 中的特权用户\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e容器主要的部分\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003enamespace\u003c/code\u003e\n\u003cul\u003e\n\u003cli\u003eUTS, IPC, PID, NETWORK, MOUNT, USER\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecgroup\u003c/code\u003e\n\u003cul\u003e\n\u003cli\u003eCPU, Memory, Blkio, Device\u0026hellip;\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eUnionFS\u003c/code\u003e\n\u003cul\u003e\n\u003cli\u003eaufs(ubuntu), btrfs(suse), vfs, devicemapper(centos), overlayer2(centos,ubuntu)\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eveth\u003c/code\u003e\n\u003cul\u003e\n\u003cli\u003ebridge, host, container, none\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e容器实现\n\u003cul\u003e\n\u003cli\u003e容器是特殊的进程\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint clone(int (*fn)(void*), void *child_stack, int flags, void*arg);\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eFork = Clone + CLONE_VM | CLONE_VFORK | SIGHILD (NOTE: Fork和Clone都是对sys_clone的封装，所以其实这里表达有少许不准确)\u003c/li\u003e\n\u003cli\u003efork是从调用点继续执行，clone是从fn(args)继续执行，因为子进程和父进程共享内存，但是维护单独的变量副本，所以需要为子进程单独分配栈，就是\u003ccode\u003echild_stack\u003c/code\u003e指针指向的位置，flags有两个作用，低位字节可以放返回信号，flags和docker相关的flag就是上述的UTS, IPC, PID, NETWORK, MOUNT, USER对应的标签\u003ccode\u003eCLONE_NEWUTS\u003c/code\u003e,\u003ccode\u003eCLONE_NEWIPC\u003c/code\u003e,\u003ccode\u003eCLONE_NEWPID\u003c/code\u003e,\u003ccode\u003eCLONE_NEWNET\u003c/code\u003e,\u003ccode\u003eCLONE_NEWNS\u003c/code\u003e,\u003ccode\u003eCLONE_NEWUSER\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003ecgroup包含着对资源的控制，对应linux下是在\u003ccode\u003e/sys/fs/cgroup\u003c/code\u003e目录下\u003c/li\u003e\n\u003cli\u003eUnionFS对应overlay2FS的路径是\u003ccode\u003esudo ls /var/lib/docker/image/overlay2/layerdb/${id}\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eveth:虚拟网卡\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"[Docker] container研究"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\nhow react interact with middleware 函数式编程的思想 curry化的函数具有延迟执行的特点，不断的currying形成的middleware可以累积参数，在配合compose,这样就可以形成类似pipeline的方式来处理数据 store因为闭包的原因，applyMiddlewar完成后，所有的middleware内部拿到的store是最新的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 export default function applyMiddleware(...middlewares) { return (createStore) =\u0026gt; (...args) =\u0026gt; { const store = createStore(...args) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (...args) =\u0026gt; dispatch(...args) } //其实就是获得一个中间件执行的函数数组，一个个去执行 chain = middlewares.map(middleware =\u0026gt; middleware(middlewareAPI)) //对dispatch加强，就是通过中间件函数加强 //我们这里也就是个例子，其实就是compose把中间件一个个的合起来跑 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } 具体什么道理 看了下源码，其实applymiddleware就是curry+compose来对dispatch进行加强的一种方式。复习下curry和compose的实现\n实现一个加法 1 const add=(...args)=\u0026gt;args.reduce((res, cur) =\u0026gt; { return res + cur; }, 0) 这样我就有一个函数了，我再写一个函数\n1 const mul2 = cur =\u0026gt; cur * 2 为了和applymiddleware类似，我有一个store,当然store可以更复杂，这里就简单一个数组\n1 const store=[1,2,3] 接着需要一个compose函数，把这两个函数合起来，当然可以多个，要实现的效果如：\n1 (f1, f2, f3, f4…) =\u0026gt; value =\u0026gt; f1(f2(f3(f4(value)))); 写出来的compose是这样：\n1 const compose = (...fns) =\u0026gt; fns.reduceRight((f, g) =\u0026gt; (...args) =\u0026gt; f(g(...args))) curry化其实是在connect那边有体现，但是这里也提一下怎么实现\n1 2 3 4 const curry = (fn,...arg1)=\u0026gt;{ if(arg1.length\u0026gt;=fn.length) return fn(...arg1) return (...arg2)=\u0026gt;curry(fn,...arg1,...arg2) } ","permalink":"https://yy-tech.online/zh/post/react-with-webpack-8/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(八)"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\nwebpack4 图片，字体 代码 https://github.com/hyyfrank/webpack4 branch: feature/lesson7\n准备 我们需要做什么\n支持 jpeg, jpg,gif,png 等文件格式\n图片处理成雪碧图\n压缩图片\n字体如何下载和处理\n需要什么 loader 和 plugin\nloader:\nfile-loader:可以解析文件中的 import/require()，转成 url，把文件打到 output 目录中\nurl-loader: 类似 file-loader,但是在文件大小（单位 byte）低于指定的限制时，可以返回一个 DataURL\nimg-loader:图片最小化的 loader,它有个依赖叫 imagemin，一般会和上面两个 loader 一起做图片的压缩\nsvg-url-loader:svg 文件是 xml 字符串，使用 base-64 不是必须的，使用 utf-8 编码比 base64 有一些好处，比如编码后短一点，使用 gzip 压缩的时候效果好点，浏览器解析 utf-8 比 base64 快\n第一步：先增加对图片的处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { test: /\\.(jpe?g|png|gif)$/i, use:[ { loader: \u0026#34;url-loader\u0026#34;, options:{ name: \u0026#34;[name]-[hash:5].min.[ext]\u0026#34;, limit: 10000, // size \u0026lt;= 20KB publicPath: \u0026#34;images/\u0026#34;, outputPath: \u0026#34;images/\u0026#34; } }, ] } 第二步：处理图片压缩 这边对每一种格式都有一个 plugin，这些 plugin 都要 npm install 一下，然后具体的配置选项可以 github 上搜到，有图片优化需求的，要详细看一下各个选项\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 { test: /\\.(jpe?g|png|gif)$/i, use:[ { loader: \u0026#34;url-loader\u0026#34;, options:{ name: \u0026#34;[name]-[hash:5].min.[ext]\u0026#34;, limit: 10000, // size \u0026lt;= 20KB publicPath: \u0026#34;images/\u0026#34;, outputPath: \u0026#34;images/\u0026#34; } }, ] } 可以执行 npm run dev 查看之前图片的大小和压缩后的大小，发现确实变小了\n第三步：生成雪碧图 1 2 3 4 5 6 7 8 9 10 11 12 { loader:\u0026#39;postcss-loader\u0026#39;, options: { sourceMap: true, config: { path: __dirname + \u0026#39;/postcss.config.js\u0026#39; }, plugins: [require(\u0026#34;postcss-sprites\u0026#34;)({ spritePath: \u0026#34;./dist/images\u0026#34; })] }, }, 这里的 plugin 是为了生成雪碧图，效果如下： 第四步： 字体处理 去 google font 下载个字体，如果翻不了墙，可以自己其他地方下一个，然后用在线字体转换器转换下，然后就能转出 ttf|otf|eot|woff 各种格式，这样本地就有字体文件了，然后解析的话，使用下面这个代码就可以 1 2 3 4 5 6 7 8 9 10 11 12 13 { test: /.(ttf|otf|eot|svg|woff(2)?)(\\?[a-z0-9]+)?$/, exclude: /images/, /* dont want svg images from image folder to be included */ use: [ { loader: \u0026#39;file-loader\u0026#39;, options: { outputPath: \u0026#39;fonts/\u0026#39;, name: \u0026#39;[name][hash].[ext]\u0026#39;, }, }, ], } 做完这个，就可以在 css 中包含自己的字体，自己可以命名下\n1 2 3 4 @font-face { font-family: \u0026#34;frankfont\u0026#34;; src: url(\u0026#34;../fonts/RobotoCondensed-Regular.woff2\u0026#34;) format(\u0026#34;woff2\u0026#34;), url(\u0026#34;../fonts/RobotoCondensed-Regular.woff\u0026#34;) format(\u0026#34;woff\u0026#34;); } 然后我们看一下使用的情况：\n1 2 3 4 5 6 7 8 .hello { font-family: frankfont Monaco Arial, Verdana, Tahoma, sans-serif; font-size: 20px; width: 300px; font-weight: bold; color: var(--color-black); background: rgba(153, 221, 153, 0.8); } 具体的效果：\n可以看到 hello css module 这句话，已经使用我们自己的字体了，图片压缩的效果和雪碧图生成效果如下：\n最后 OK，好了，大概该做的都做完了，这基本任务算是都能处理了，其实还有好多东西要做，有空会多写一些。\n","permalink":"https://yy-tech.online/zh/post/react-with-webpack-6/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(六)"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\nWebpack4 如何处理 css github demo branch: feature/lesson5 我们需要做什么 支持 css 的处理 把 css 抽取成单独的 css 文件 支持 css module 支持 css next 等新特性 支持 css style lint 校验 减少无用的 css 代码 使用 post css 做些处理比如 autoprefix,css-next 最小化 css 文件 需要什么 loader 和 plugin loader 用于对模块的源代码进行转换。loader 可以使你在 import 或\u0026quot;加载\u0026quot;模块时预处理文件。因此，loader 类似于其他构建工具中“任务(task)”，并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言（如 TypeScript）转换为 JavaScript，或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件！\nloader: sass-loader: 简单说就是 sass 转换成 css postcss-loader: postcss 很强大，可以这么简单理解下，css\u0026ndash;\u0026gt;ast\u0026ndash;\u0026gt;plugin\u0026ndash;\u0026gt;xxxx,大概就是，先把 css 转化为抽象语法树，然后使用 javascript 处理，然后厉害的就是，真的啥都有个插件，只有想不到! css-loader:**The css-loader interprets @import and url() like import/require() and will resolve them.**选项我们会用到 modules, localIdentName style-loader:Adds CSS to the DOM by injecting a \u0026lt;style\u0026gt; tag plugin: mini-css-extract-plugin：抽取 css 成单独文件 purifycss-webpack： 删除没使用的 css 选择器 stylelint-webpack-plugin：对 css 做 lint optimize-css-assets-webpack-plugin:webpack 在 build 的过程中优化和最小化 css，默认使用 cssnano 做预处理器，cssnano 也是个 postcss 的 plugin 好了，废话到此为止，上代码看下就都明了，为了让代码更清晰，我们把对 css 的处理单独拉出来，再通过 webpack-merge 合到一起，下面是 css 的处理代码： 抽取代码成单独文件，现在目测原来的 extract-text-webpack-plugin 还是能用的，如果是使用 webpack4 的话，也得更新 extract-text-webpack-plugin 的版本到^4.0.0-beta.0，下面注释掉的，大家可以加回来试试，因为 webpack4 推荐使用 mini-css-extract-plugin，所以我们就使用这个,而且 mini-css-extract-plugin 不支持 hmr,但是不是在开发环境，我们只放在生产环境，其实还是 ok 的。 第一步：support style-loader,css-loader,sass-loader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const cssDevRules=[ { loader:\u0026#39;style-loader\u0026#39; }, { loader:\u0026#39;css-loader?modules\u0026amp;localIdentName=[name]_[local]_[hash:base64:5]\u0026#39;, }, { loader:\u0026#39;sass-loader\u0026#39;, } ]; const cssProdRules=[ { loader: MiniCssExtractPlugin.loader, }, { loader:\u0026#39;css-loader?modules\u0026amp;localIdentName=[name]_[local]_[hash:base64:5]\u0026#39;, }, { loader:\u0026#39;sass-loader\u0026#39;, } ]; console.log(\u0026#34;is prod:\u0026#34;+isProd); const baseConfig = { module: { rules: [ { test: /\\.(css|sass|scss)$/, use: isProd? cssProdRules:cssDevRules, exclude: /node_modules/, }, ] }, plugins: [ ], }; css支持 第二步：css-next /autoprefixer support 加上 posts-loader 来支持 auto-prfix 自动增加，同时，为了支持 css-next 最新的特性，现在不需要单独加，看官方文档有个 postcss-preset-env，直接加了支持 autoprefixer,PostCSS Preset Env 能把现代的 css 转化成大部分浏览器都能解析的样式，会根据浏览器的版本决定加什么样的 polyfill.\n1 npm install postcss-preset-env 简单修改下 postcss.config.js\n1 2 3 4 5 6 7 8 9 module.exports = { plugins: { \u0026#34;postcss-import\u0026#34;: {}, \u0026#34;postcss-preset-env\u0026#34;: { browsers: \u0026#34;last 2 versions\u0026#34; }, cssnano: {} } }; 1 2 3 4 5 6 7 8 9 { loader:\u0026#39;postcss-loader\u0026#39;, options: { sourceMap: true, config: { path: __dirname + \u0026#39;/postcss.config.js\u0026#39; } }, }, 风格校验 第三步：add style-lint support 加 style-lint 来控制代码质量，当然我只是加上而已，具体规则要自己去定，或者你可以用标准的 stylelint-config-standard,对应的 plugin 是 stylelint-config-standard\n1 2 3 4 5 6 7 8 9 const StyleCssLintPlugin = require(\u0026#34;stylelint-webpack-plugin\u0026#34;); const StyleLintPlugin = new StyleCssLintPlugin({ configFile: \u0026#34;.stylelintrc\u0026#34;, context: \u0026#34;src\u0026#34;, files: \u0026#34;**/*.scss\u0026#34;, failOnError: false, quiet: false }); baseConfig.plugins = [StyleLintPlugin, MiniCssPlugin, OptimizeCSSPlugin]; 当然也要加个.stylelintrc 文件,现在简单先用 stylelint-config-standard。\n1 2 3 { \u0026#34;extends\u0026#34;: \u0026#34;stylelint-config-standard\u0026#34; } 移除冗余css 第四步：remove unused css purifycss： 移除无用 css,有人使用 css tree shake 这种术语，anyway,感觉差不多\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const PurifyCSSPlugin = require(\u0026#34;purifycss-webpack\u0026#34;); const PurifyCssPlugin = new PurifyCSSPlugin({ paths: glob.sync(path.join(__dirname, \u0026#34;../src/index.js\u0026#34;)), styleExtensions: [\u0026#34;.css\u0026#34;, \u0026#34;.scss\u0026#34;], purifyOptions: { whitelist: [\u0026#34;*purify*\u0026#34;] } }); baseConfig.plugins = [ MiniCssPlugin, PurifyCssPlugin, StyleLintPlugin, OptimizeCSSPlugin // new ExtractTextPlugin(\u0026#34;styles.css\u0026#34;), ]; 最小化CSS 第五步：minimize css optimize-css-assets-webpack-plugin cssProcessor: 压缩和优化 CSS 的预处理器，现在默认是 cssnano.这是一个函数，接受一个 CSS 和 options 参数，返回 promise canPrint: {bool} 表示插件能够在 console 中打印信息，默认值是 true\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const OptimizeCSSAssetsPlugin = require(\u0026#34;optimize-css-assets-webpack-plugin\u0026#34;); const OptimizeCSSPlugin = new OptimizeCSSAssetsPlugin({ cssProcessor: cssnano, cssProcessorOptions: { discardComments: { removeAll: true }, // Run cssnano in safe mode to avoid // potentially unsafe transformations. safe: true }, canPrint: true }); baseConfig.plugins = [ MiniCssPlugin, PurifyCssPlugin, StyleLintPlugin, OptimizeCSSPlugin // new ExtractTextPlugin(\u0026#34;styles.css\u0026#34;), ]; 最后 OK，好了，大概该做的都做完了，如果需要进一步处理，可以考虑 postcss 的一些 plugin，甚至可以自己写点 plugin,因为这是讲 webpack 不是 postcss，所以留给你自己探索吧～\n","permalink":"https://yy-tech.online/zh/post/react-with-webpack-5/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(五)"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\nwebpack4 with Babel, React, CSS Module 这节主要讲下 babel, react, css module 的简单引入，后续会根据需要再来修改，我们先做一个版本 需要什么 首先，看下需要装哪些包，都是干什么的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 \u0026#34;dependencies\u0026#34;: { \u0026#34;react\u0026#34;: \u0026#34;^16.8.1\u0026#34;, //react package \u0026#34;react-dom\u0026#34;: \u0026#34;^16.8.1\u0026#34;,//the entry point to the DOM and server renderers for React \u0026#34;webpack\u0026#34;: \u0026#34;^4.29.0\u0026#34;, // polyfill: you can use Promise, WeakMap,Array.from,Object.assign,Array.includes.. // this is a polyfill, we need it to be a dependency \u0026#34;@babel/polyfill\u0026#34;: \u0026#34;^7.2.5\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { //Compile object rest and spread to ES5 \u0026#34;@babel/plugin-proposal-object-rest-spread\u0026#34;: \u0026#34;^7.3.2\u0026#34;, // re-use of Babel\u0026#39;s injected helper code to save on codesize. \u0026#34;@babel/plugin-transform-runtime\u0026#34;: \u0026#34;^7.2.0\u0026#34;, //a library that contain\u0026#39;s Babel modular runtime //helpers and a version of regenerator-runtime. \u0026#34;@babel/runtime\u0026#34;: \u0026#34;^7.0.0-beta.55\u0026#34;, //babel comman line tool. \u0026#34;@babel/cli\u0026#34;: \u0026#34;^7.2.3\u0026#34;, \u0026#34;@babel/core\u0026#34;: \u0026#34;^7.2.2\u0026#34;, //a smart preset that allows you to use the latest JavaScript without needing to //micromanage which syntax transforms \u0026#34;@babel/preset-env\u0026#34;: \u0026#34;^7.3.1\u0026#34;, //@babel/plugin-syntax-jsx //@babel/plugin-transform-react-jsx //@babel/plugin-transform-react-display-name //@babel/plugin-transform-react-jsx-self //@babel/plugin-transform-react-jsx-source \u0026#34;@babel/preset-react\u0026#34;: \u0026#34;^7.0.0\u0026#34;, \u0026#34;babel-loader\u0026#34;: \u0026#34;^8.0.5\u0026#34;, \u0026#34;babel-plugin-transform-object-rest-spread\u0026#34;: \u0026#34;^6.26.0\u0026#34;, \u0026#34;css-loader\u0026#34;: \u0026#34;^2.1.0\u0026#34;, \u0026#34;html-loader\u0026#34;: \u0026#34;^0.5.5\u0026#34;, \u0026#34;style-loader\u0026#34;: \u0026#34;^0.23.1\u0026#34;, \u0026#34;html-webpack-plugin\u0026#34;: \u0026#34;^4.0.0-beta.5\u0026#34;, \u0026#34;clean-webpack-plugin\u0026#34;: \u0026#34;^1.0.1\u0026#34;, \u0026#34;webpack-cli\u0026#34;: \u0026#34;^3.2.1\u0026#34;, \u0026#34;webpack-dev-server\u0026#34;: \u0026#34;^3.1.14\u0026#34; } 这里要注意的是@babel/polyfill，这个要放在 dependencies 里边，因为是 polyfill,代码最后也是要在里边的，所以不能放在 devDependencies 里。接着看 babel 的配置文件。.babelrc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { \u0026#34;presets\u0026#34;: [ [\u0026#34;@babel/preset-env\u0026#34;, { \u0026#34;targets\u0026#34;: { \u0026#34;node\u0026#34;: \u0026#34;current\u0026#34; } }], [\u0026#34;@babel/preset-react\u0026#34;] ], \u0026#34;plugins\u0026#34;: [ [\u0026#34;@babel/plugin-transform-runtime\u0026#34;], [\u0026#34;@babel/plugin-proposal-object-rest-spread\u0026#34;,{ \u0026#34;useBuiltIns\u0026#34;: true }] ] } 增加react支持 webpack.config.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 const path = require(\u0026#34;path\u0026#34;); const webpack = require(\u0026#34;webpack\u0026#34;); const HtmlWebpackPlugin = require(\u0026#34;html-webpack-plugin\u0026#34;); const CleanWebpackPlugin = require(\u0026#34;clean-webpack-plugin\u0026#34;); const baseConfig = { entry: [ \u0026#34;@babel/polyfill\u0026#34;, //这里要写，配置polyfill,也可以写在源码里 \u0026#34;./src/index.js\u0026#34; ], devtool: \u0026#34;cheap-module-source-map\u0026#34;, //production的source map module: { rules: [ { test: /\\.css$/, use: [ { loader: \u0026#34;style-loader\u0026#34; }, { //这里注意，要使用css module以及css module的格式 loader: \u0026#34;css-loader?modules\u0026amp;localIdentName=[name]_[hash:base64:5]\u0026#34; } ], exclude: /node_modules/ }, { test: /\\.(js|jsx)$/, exclude: /node_modules/, use: { //js/jsx使用babel-loader来处理 loader: \u0026#34;babel-loader\u0026#34; } }, { test: /\\.html$/, use: [ { loader: \u0026#34;html-loader\u0026#34; } ] } ] }, plugins: [ new CleanWebpackPlugin([\u0026#34;dist\u0026#34;]), new HtmlWebpackPlugin({ template: path.resolve(__dirname, \u0026#34;src\u0026#34;, \u0026#34;index.html\u0026#34;), //模板 filename: \u0026#34;index.html\u0026#34;, hash: true //防止缓存 }), new webpack.HotModuleReplacementPlugin() ], resolve: { extensions: [\u0026#34;*\u0026#34;, \u0026#34;.js\u0026#34;, \u0026#34;.jsx\u0026#34;] }, output: { publicPath: \u0026#34;/\u0026#34;, path: path.resolve(__dirname, \u0026#34;dist\u0026#34;), filename: \u0026#34;[name]-bundle.js\u0026#34; } }; //增加这部分，当是开发环境的时候，再使用hmr和inline-source-map. if (process.env.NODE_ENV === \u0026#34;development\u0026#34;) { baseConfig.devtool = \u0026#34;inline-source-map\u0026#34;; baseConfig.devServer = { contentBase: \u0026#34;./dist\u0026#34;, hot: true, open: true }; } module.exports = baseConfig; 第一个JSX 然后写个简单的 jsx 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import React from \u0026#34;react\u0026#34;; import * as style from \u0026#34;../css/main.css\u0026#34;; const HomeComponent = () =\u0026gt; { //测试对象展开 let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; console.log(x); // 1 console.log(y); // 2 console.log(z); // { a: 3, b: 4 } //测试array.from const arr = Array.from(new Set([1, 2, 3, 2, 1])); const arr2 = [1, [2, 3], [4, [5]]].flat(2); console.log(arr2); //测试下promise const promise = new Promise((resolve, reject) =\u0026gt; { console.log(\u0026#34;promise\u0026#34;); resolve(1); }); //测试下symbol const sym = Symbol(); console.log(\u0026#34;symbol:\u0026#34; + sym.toString()); return ( \u0026lt;div\u0026gt; \u0026lt;h2\u0026gt;Hello React16.7.0!\u0026lt;/h2\u0026gt; \u0026lt;div className={style.hello}\u0026gt;Hello CSS Module!\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ); }; export default HomeComponent; 到这里就能让 react 的一个最简单的例子跑起来了,看下 index.js\n1 2 3 4 import React from \u0026#34;react\u0026#34;; import ReactDOM from \u0026#34;react-dom\u0026#34;; import HomeComponent from \u0026#34;./components/home\u0026#34;; ReactDOM.render(\u0026lt;HomeComponent /\u0026gt;, document.getElementById(\u0026#34;app\u0026#34;)); package.json 里的 scripts 这样写\n1 2 3 4 \u0026#34;scripts\u0026#34;: { \u0026#34;prod\u0026#34;: \u0026#34;webpack --mode production\u0026#34;, \u0026#34;dev\u0026#34;: \u0026#34;NODE_ENV=development webpack-dev-server --mode development --open\u0026#34; }, 结论 可以看到，我们只是简单把一个react需要引入的东西引入进来，接下来，我们在这个最简单的例子上，逐步增加支持，让我们的应用更加professional\n","permalink":"https://yy-tech.online/zh/post/react-with-webpack-4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(四)"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\n基础 entry: 配置模块的入口 webpack 寻找文件的时候会以 context 为基础，context 默认的是执行 webpack 的路径，比如我们项目就是默认根目录，当然也可以采取命令行去配置，比如 webpack \u0026ndash;context 1 2 3 module.exports = { context: path.resolve(__dirnaame, \u0026#34;app\u0026#34;) }; 我们现在看到的 entry 里的路径也是相对于这个 context 的路径的，这个选项会影响后续配置的文件的路径 entry 可以配成三种方式 string, array, object,比如我们现在是一个页面就是‘./src/index.js’,多个页面的入口就是[\u0026rsquo;./src/firstpage.js\u0026rsquo;,\u0026rsquo;./src/secondpage.js'] chunk: webpack 会为每个生成的 chunk 起名字，如果上面 entry 配了 string/array,则只有一个 chunk,如果配成 object,那么 chunk 会有多个，每个的名字就是 object 里的 key 的名字: 配置动态 entry: 这个就是如果你有多个页面，要做多个入口，可以写成动态的，比如同步的直接返回一个 object，异步方式就返回一个 promise 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //同步 entry: () =\u0026gt; { return { first: \u0026#34;./src/firstpage\u0026#34;, second: \u0026#34;./src/sencordpage\u0026#34; }; }; //异步 entry: () =\u0026gt; { return new Promise(resolve =\u0026gt; { resolve({ first: \u0026#34;./src/firstpage\u0026#34;, second: \u0026#34;./src/secondpage\u0026#34; }); }); }; output:配置如何输出最终需要的代码 filename:如果只有一个，就是字符串，比如我们的 bundle.js,如果输出多个 chunk,就用通配符[name].js, 可以这样[id]-[name]-[hash]-[chunkhash].js,这几个都是他的内部变量，表示 chunk 的唯一标示，名称，唯一标示的 hash,chunk 内容的 hash chunkFilename:比如 commonchunkplugin 输出的文件名，内置变量和上面一致 path：打包文件的输出目录 publicpath：这个就是静态资源如果放 cdn 上，需要去配的，可以自己写个域名放上去看看效果就知道了 crossOriginLoading：输出的时候如果需要异步加载一些资源，这个就是配置这些资源的获取，一般是通过 jsonp 来做的，会往 html 里插一个 libraryTarget \u0026amp; library：配置以什么方式导出库和导出库的名称 libraryExport： 如果上面导出方式是 commons/commonjs2 的时候，你可以在这里导出你想导出的字模块 module:配置处理模块的规则 rules 配置模块的读取和解析规则，就是配置 loader 的时候，一般是一个数组，然后每一部分配置怎么处理一个类型的文件，每一部分包括三个方面 匹配到需要处理的文件，包括 test(支持数组), include, exclude 使用特定的 loader 来处理这些匹配到的文件 babel-loader, css-loader\u0026hellip;loader 如果有多个参数需要传入，可以使用 object 来传递，经常看到的是 option:{xxxx}这样的 执行 loader 的顺序可以调整到最开始或者最后执行，通过 enforce 来配置，设为 pre/post noParse: 这个就是用来让 webpack 忽略一些文件，比如 jquery/chartjs,举个例子： 1 2 3 4 5 noParse:|jquery/chartjs/ // 或者是函数形式 noParse: (content) =\u0026gt;{ return /jquery/chartjs/.test(content) } parser: 支持 amd, commonjs,systemjs,es6,举个例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { moudle:{ rules:{ test: /\\.js$/, use:[\u0026#39;babel:loader\u0026#39;], parser:[ amd: false, commonjs: false, system:false, harmony:false, requireInclude: false,//禁用require.include requireEnsure: false,//禁用require.ensure requireContext: false,//禁用require.context browserify: false, requireJs:false ] } } } resolve: webpack 如何寻找模块所对应的文件 alias: 就是别名，比如你 import 的时候，它会替换成真正的位置 1 2 3 4 5 resolve: { alias: { components: \u0026#34;./src/components\u0026#34;; } } 上面代码就会在你 import xxx from \u0026ldquo;components/xxx\u0026quot;的时候帮你替换成“./src/components/xxx” mainFields: 有的模块提供不同环境的代码，这个字段可以指定优先使用哪个版本 extentions: 让 webpack 在寻找文件的时候，找这样扩展名的文件，你导入文件的时候不指定后缀名的话，会去看这个选项的配置，然后去寻找 1 2 // 先去找ts文件，然后js,然后json文件 extentsions:[\u0026#39;.ts\u0026#39;,\u0026#39;.js\u0026#39;,\u0026#39;.json\u0026#39;] modules: resolve.modules 配置 web pack 去哪些目录寻找第三方模块，默认指定 node_modules,一般我们应用会这样配 1 modules:[\u0026#39;./src/components\u0026#39;,\u0026#39;node_modules\u0026#39;] 这样配后，你以前可能需要 import xxx from ../../../components/xxx 就可以直接 import xxx from xxx;简洁了许多 enforeExtention: 如果配成 true,那么你 import 语句就必须加后缀名，否则会找不到 plugins:配置扩展插件 我们项目里用到了一些，你也可以看到，其实引入都是大同小异，主要是要搞清楚这里面的具体的配置项 dev-server:配置 dev-server hot: 我们在配置 hot module replacement 的时候会配的 inline： 一般使用这个模式，打开之后，webpack 通过代理客户端控制模块替换和刷新，如果关闭，那它文件变化后，会通过 iframe 的方式去运行，要到 localhost:8080/webpack-dev-server 看效果 还有一些配置项，参考文档，比如 historyApiFallback, contentBase ,headers ,host, port, allowHosts ,disableHostCheck, https,clientLogLevel,compress,open 这些都可以去看看，经常涉及到 others:其他配置项，配置文件不止可以返回 object,也可以返回其他形式 target: 针对不同的环境，比如 web, node,async-node,webworker,electron-main,electron-renderer Devtool: 配置 webpack 如何生成 sourcemap watch: 配置文件更新监听 external：有些第三方库，这些不需要 webpack 打包，比如 jquery ResolveLoader: 告诉 webpack 如何发现 loader ","permalink":"https://yy-tech.online/zh/post/react-with-webpack-3/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(三)"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\nversion in package.json package.json 里的^和~有啥区别 这个其实就是说明你项目中可以用哪个版本的软件，例如：3.4.5 类似 MAJOR.MINOR.PATCH 这种格式，这个叫 sematic versioning, 官网地址(https://semver.org/)。 MAJOR: 引入了不向后兼容的 API MINOR:引入了向后兼容的 API. PATCH:修 bug 的版本 举个例子，一般发布 API 版本从 1.0.0，修了四个 bug,变成 1.0.4，引入新的 API,但是这些 API 向后兼容，那就变成 1.1.0，再修两个 bug,就变成 1.1.2，如果新的版本加入了新 API 是不向后兼容的，可能会破坏依赖，这种版本就是 2.0.0，以此类推 package.json 的情况是，~3.4.5 就是指 3.4.x 这样的版本，但是不超过 3.5.0， ^3.4.5 的意思就是 3.x.x 都是，但是低于 4.0.0 的版本。这样说就好理解了(具体看我上面的提供的链接)。 我们常见的 npm install antd \u0026ndash;save 通常你在 package.json 看到的是^3.13.0 就是你可以使用 3.x.x 的版本但低于 4.0.0.当然有规则就有例外，如果是 0 开头的，有些许不同，简单讲，可以将^理解成~就成了，具体官方网页瞄一眼就明白了。 再说 hot replacement module(HRM)之前，需要了解几个东西，我们在上次的例子里来继续做： html-webpack-plugin html-webpack-plugin: 这个 plugin 可以帮我们生成 html 文件，比如如果不使用他，你打包了，js 都到 dist 目录下(官方喜欢用 dist，我们就把 build 改成 dist),那你的 html 是不是还要自己手动引用这个新生成的地址，怎么自动化也帮我们把 html 生成并且把生成的 dist 目录下的 js 也引用进来呢，这个 plugin 就做这个事情，代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 plugins: [ new HtmlWebpackPlugin({ //模板,可以指定模板，但是要指定loader,这里我们用html-loader template: path.resolve(__dirname, \u0026#34;src\u0026#34;, \u0026#34;index.html\u0026#34;), //输出文件的文件名字，默认就是index.html,路径是相对于webpackConfig.output.path路径 filename: \u0026#34;index.html\u0026#34;, //防止缓存,也就是生成的时候引用的时候，会有一个参数，这样就每次都去加载这个js，浏览器认为这是一个新的文件，有的人会做增量更新，其实道理差不多，就是用新的名字，让浏览器强制加载新的文件 hash: true, //压缩的选项，字面意思，不知道的话，可以看下官网 minify: { collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true, removeAttributeQuotes: true }, meta: { viewport: \u0026#34;width=device-width, initial-scale=1, shrink-to-fit=no\u0026#34;, \u0026#34;theme-color\u0026#34;: \u0026#34;#4285f4\u0026#34; } }) ]; 生成了 html 长这样,第一图是压缩的，第二图是 format 之后，因为我们制定了压缩，所以生成第一图这样的代码，这里要注意 webpack.config.js 里的 publicpath,如果指定，那么，生成的 js 的地址会变成 publicpath/bundle.js?71ac66103d2a 这样的引用\n1 2 3 4 5 6 7 8 9 10 11 12 13 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt;\u0026lt;/html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=UTF-8\u0026gt; \u0026lt;meta name=viewport content=\u0026#34;width=device-width,initial-scale=1\u0026#34;\u0026gt; \u0026lt;meta http-equiv=X-UA-Compatible content=\u0026#34;ie=edge\u0026#34;\u0026gt; \u0026lt;meta name=viewport content=\u0026#34;width=device-width,initial-scale=1,shrink-to-fit=no\u0026#34;\u0026gt; \u0026lt;meta name=theme-color content=#4285f4\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt;HTML WEBPACK PLUGIN TEMPLATE. \u0026lt;script src=bundle.js?71ac66103d2a01102753\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; clean-webpack-plugin 我们之前有一个删除目录的npm包，webpack里也有相应的plugin可以做这个事情，代码如下：\n1 2 3 plugins:[ new CleanWebpackPlugin([\u0026#39;dist\u0026#39;]), ] css plugin 关于 css, 本来想使用 mini-css-extract-plugin，但是这个 plugin 目前不支持 HRM,所以用老的就行\n1 2 3 4 5 6 7 8 9 module: { rules: [ { test: /\\.css$/, use: [\u0026#39;style-loader\u0026#39;, \u0026#39;css-loader\u0026#39;] } ] } webpack-dev-server 然后要配置 webpack-dev-server 和 HotModuleReplacementPlugin\n1 new webpack.HotModuleReplacementPlugin(); Dev_server_plugin 在 webpack 里这样配：\n1 2 3 4 devServer: { contentBase: \u0026#39;./dist\u0026#39;, hot: true }, final package.json 所有安装的 plugin 都需要 cnpm install,package.json 如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 { \u0026#34;name\u0026#34;: \u0026#34;webpack4\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;1.0.0\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;index.js\u0026#34;, \u0026#34;repository\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;git\u0026#34;, \u0026#34;url\u0026#34;: \u0026#34;git+https://github.com/hyyfrank/webpack4.git\u0026#34; }, \u0026#34;keywords\u0026#34;: [], \u0026#34;author\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;license\u0026#34;: \u0026#34;ISC\u0026#34;, \u0026#34;bugs\u0026#34;: { \u0026#34;url\u0026#34;: \u0026#34;https://github.com/hyyfrank/webpack4/issues\u0026#34; }, \u0026#34;scripts\u0026#34;: { \u0026#34;build\u0026#34;: \u0026#34;webpack --watch\u0026#34;, \u0026#34;dev\u0026#34;: \u0026#34;webpack-dev-server\u0026#34; }, \u0026#34;homepage\u0026#34;: \u0026#34;https://github.com/hyyfrank/webpack4#readme\u0026#34;, \u0026#34;dependencies\u0026#34;: { \u0026#34;webpack\u0026#34;: \u0026#34;^4.29.0\u0026#34; }, \u0026#34;devDependencies\u0026#34;: { \u0026#34;clean-webpack-plugin\u0026#34;: \u0026#34;^1.0.1\u0026#34;, \u0026#34;css-loader\u0026#34;: \u0026#34;^2.1.0\u0026#34;, \u0026#34;html-loader\u0026#34;: \u0026#34;^0.5.5\u0026#34;, \u0026#34;html-webpack-plugin\u0026#34;: \u0026#34;^4.0.0-beta.5\u0026#34;, \u0026#34;style-loader\u0026#34;: \u0026#34;^0.23.1\u0026#34;, \u0026#34;webpack-cli\u0026#34;: \u0026#34;^3.2.1\u0026#34;, \u0026#34;webpack-dev-server\u0026#34;: \u0026#34;^3.1.14\u0026#34; } } final webpack.config.js webpack.config.js 最后长这样：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const path = require(\u0026#34;path\u0026#34;); const webpack = require(\u0026#34;webpack\u0026#34;); const HtmlWebpackPlugin = require(\u0026#34;html-webpack-plugin\u0026#34;); const CleanWebpackPlugin = require(\u0026#34;clean-webpack-plugin\u0026#34;); module.exports = { entry: { app: \u0026#34;./src/index.js\u0026#34; }, devtool: \u0026#34;inline-source-map\u0026#34;, devServer: { contentBase: \u0026#34;./dist\u0026#34;, hot: true }, mode: \u0026#34;development\u0026#34;, module: { rules: [ { test: /\\.css$/, use: [\u0026#34;style-loader\u0026#34;, \u0026#34;css-loader\u0026#34;] } ] }, plugins: [ new CleanWebpackPlugin([\u0026#34;dist\u0026#34;]), new HtmlWebpackPlugin({ template: path.resolve(__dirname, \u0026#34;src\u0026#34;, \u0026#34;index.html\u0026#34;), //模板 filename: \u0026#34;index.html\u0026#34;, hash: true //防止缓存 }), new webpack.HotModuleReplacementPlugin() ], output: { publicPath: \u0026#34;/\u0026#34;, path: path.resolve(__dirname, \u0026#34;dist\u0026#34;), filename: \u0026#34;[name]-bundle.js\u0026#34; } }; 最后效果 打开浏览器的inspector,到console面板里，看到有个字写[WDS] Hot Module Replacement enabled.说明 hrm 是好的，你修改下 JS 会发现立马自己更新。\n","permalink":"https://yy-tech.online/zh/post/react-with-webpack-2/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(二)"},{"content":" 这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架 如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术 也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置， 热部署，调试环境等等问题\nwebpack scaffold 环境 第一：开发环境 使用 vscode, github 概念啥的很多地方都讲过了，这里水一下，请看文档 OK, 接着开始讲怎么用，一般项目需要的功能，我们一个个做,先做个例子，看看打包大概流程是怎么样的，不 bb.\n先用 npm 初始化项目，当然用 yarn 也行，执行 npm -init -y 就生成 package.json 文件了(最好换 cnpm 稍微快点)\n先去 vscode 的 plugin 下一个 html boilerplate 然后写 html 就舒服了，先写个 html,这时候只要简单建个文件输入 html:5 回车，就得到一个简单的 html5 文件了\n简单加个 css 文件和 js 文件，那三剑客就齐了，这也是最网页最简单的样子了\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;meta http-equiv=\u0026#34;X-UA-Compatible\u0026#34; content=\u0026#34;ie=edge\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;./main.css\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Hello Webpack\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h2\u0026gt;Say hello to webpack4!\u0026lt;/h2\u0026gt; \u0026lt;script src=\u0026#34;./bundle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; http-server 为了简单看到 html 网页的效果，我们装个库：cnpm install http-server -g 我要用 webpack 自然得装下，cnpm install webpack -g 启动网页，用我们刚才得 http-server: http-server -p 3000, 访问 http://127.0.0.1:3000/ 就能看到简单页面 webpack入坑 简单网页打完收工。接着就是来使用 webpack 来打包和混淆了 js 了。\n先简单试一下：./node_modules/webpack/bin/webpack.js ./main.js bundle.js 然后你去把 html 里的 js 的 src 改成 bundle.js，打开网页看下，也是好的，说明 webpack 已经把 main.js 打包成 bundle.js 了，你可以去看下里边代码长什么样，有个了解 当然，我不能每次都自己动手去改这个 html 和输入命令吧，所以我们 webpack 提供了配置文件的方式来让你写，结合 package.json 里的 scripts 部分，可以让我们加命令，这样就不会敲命令敲到手软了 开始正式打包 web pack.config.js 长这样： 1 2 3 4 5 6 7 8 9 10 11 const path = require(\u0026#34;path\u0026#34;); module.exports = { entry: { app: \u0026#34;./src/main.js\u0026#34; }, output: { publicPath: __dirname + \u0026#34;/build/\u0026#34;, // js引用路径或者CDN地址 path: path.resolve(__dirname, \u0026#34;build\u0026#34;), // 打包文件的输出目录 filename: \u0026#34;bundle.js\u0026#34; } }; 结果 和我们刚才命令行差不多，不过这次用配置文件的方式展示出来,啥意思呢，就是上面注释那意思。什么，你不知道 entry 和 output 的意思，拜托，稍微看下文档，中文的也行啊，起码有个大概了解【参考文章开始的链接】，写完这个，直接在当前目录下打 webpack 命令搞定！然后我们把命令放在 package.json 里的 scripts 部分，以后直接 npm run build 就跑这个构建命令，就方便了，如下：(这里 webpack 是全局装的，方便点) 把命令放进去package.json 1 2 \u0026#34;build\u0026#34;: \u0026#34;webpack\u0026#34; OK,那我每次执行完构建，会生成一个 build 目录，我不想每次自己手动去删掉，所以，我们可以构建之前先删除这个文件，然后再开始构建，简单装个酷，cnpm install rmdir-cli,然后我们的 package.json 里的 build 脚本变成： 1 \u0026#34;build\u0026#34;: \u0026#34;rmdir-cli build \u0026amp;\u0026amp; webpack --watch\u0026#34; 稍微调整下文件结构，加个 src 目录来放 js, 只需要修改下 webpack.config.js 里的 entry 的路径就可以了,当然了你 html 如果要应用到文件，js 的 src 要改成从 build 中去取就行了，详细代码看我的 github repo,watch 就是为了你能监听文件的变化，修改文件 webpack 重新编译你也能看到 各种规范打包出来的样子 因为支持 webpack 支持 ES6、CommonJs 和 AMD 规范，所以都可以在 js 里去写，举个栗子，加上 es6：\nmain.js\n1 2 3 4 5 import addtwo from \u0026#34;./add\u0026#34;; console.log(\u0026#34;javascript say hello.\u0026#34;); addtwo(1, 2); add.js\n1 2 3 4 5 export default (a, b) =\u0026gt; { console.log(\u0026#34;a+b =\u0026#34;, a + b); return a + b; }; 重新构建下，跑一下，看看打开 html 里的 chrome dev tool,看看 a+b=3 有没有打出来。搞定！\n看一眼打完包是什么样子\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 !(function(e) { var t = {}; function r(n) { if (t[n]) return t[n].exports; var o = (t[n] = { i: n, l: !1, exports: {} }); return e[n].call(o.exports, o, o.exports, r), (o.l = !0), o.exports; } (r.m = e), (r.c = t), (r.d = function(e, t, n) { r.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: n }); }), (r.r = function(e) { \u0026#34;undefined\u0026#34; != typeof Symbol \u0026amp;\u0026amp; Symbol.toStringTag \u0026amp;\u0026amp; Object.defineProperty(e, Symbol.toStringTag, { value: \u0026#34;Module\u0026#34; }), Object.defineProperty(e, \u0026#34;__esModule\u0026#34;, { value: !0 }); }), (r.t = function(e, t) { if ((1 \u0026amp; t \u0026amp;\u0026amp; (e = r(e)), 8 \u0026amp; t)) return e; if (4 \u0026amp; t \u0026amp;\u0026amp; \u0026#34;object\u0026#34; == typeof e \u0026amp;\u0026amp; e \u0026amp;\u0026amp; e.__esModule) return e; var n = Object.create(null); if ( (r.r(n), Object.defineProperty(n, \u0026#34;default\u0026#34;, { enumerable: !0, value: e }), 2 \u0026amp; t \u0026amp;\u0026amp; \u0026#34;string\u0026#34; != typeof e) ) for (var o in e) r.d( n, o, function(t) { return e[t]; }.bind(null, o) ); return n; }), (r.n = function(e) { var t = e \u0026amp;\u0026amp; e.__esModule ? function() { return e.default; } : function() { return e; }; return r.d(t, \u0026#34;a\u0026#34;, t), t; }), (r.o = function(e, t) { return Object.prototype.hasOwnProperty.call(e, t); }), (r.p = \u0026#34;/Users/hyy/github/webpack4/build/\u0026#34;), r((r.s = 0)); })([ function(e, t, r) { \u0026#34;use strict\u0026#34;; r.r(t); console.log(\u0026#34;javascript say hello.\u0026#34;), ((e, t) =\u0026gt; (console.log(\u0026#34;a+b =\u0026#34;, e + t), e + t))(1, 2); } ]); 今天困了，先写到这吧，不知道说啥了，扯个淡收场吧！\n","permalink":"https://yy-tech.online/zh/post/react-with-webpack-1/","summary":"\u003cblockquote\u003e\n\u003cp\u003e这是一个webpack4的前端架构主题，主要涉及怎么从0到1构建前端脚手架\n如何把最新流行的技术整合在一起,涉及react,redux,webpack4相关技术\n也包含一些项目构建需要的东西，比如gitignore,code format，不同环境设置，\n热部署，调试环境等等问题\u003c/p\u003e","title":"React With Webpack(一)"},{"content":"如何查日志 1 sudo /var/log/syslog | grep cron 捕获输出 1 1 2 * * * /home/hyy/Start.py \u0026gt;/tmp/output.log 2\u0026gt;\u0026amp;1 确认 cron 在跑 1 ps -ef | grep cron | grep -v grep 路径要对 crontab 默认在 $HOME 下执行；若脚本里用 os.getcwd()，目录不对会报找不到模块。 命令里尽量写绝对路径，或在任务前 source ~/.zshrc，保证环境变量可用。 最后一行要空行 很多人踩坑：crontab 文件末尾需要空行，否则最后一条可能不执行。 Debian/Ubuntu 的权限与命名 /etc/cron.d 及 cron.{hourly,daily,weekly,monthly} 下的文件需满足：\n属主为 root； 仅 root 可写； 组和其他用户不可写； 文件名不要包含 .，除 -、_ 外不要有特殊字符。 Python 脚本注意点 可在脚本首行加 #!/usr/bin/python（非必须）； 一定要把输出重定向到日志，方便排查。 脚本权限 尽量用普通用户 crontab，不要用 sudo crontab -e。 若日志被 root 删掉又 root 新建，权限会错；正确做法是 echo \u0026quot;\u0026quot; \u0026gt; logfile，保留原文件权限与属组。 ","permalink":"https://yy-tech.online/zh/post/play-with-contab/","summary":"\u003ch2 id=\"如何查日志\"\u003e如何查日志\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo /var/log/syslog \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep cron\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"捕获输出\"\u003e捕获输出\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e \u003cspan class=\"m\"\u003e2\u003c/span\u003e * * * /home/hyy/Start.py \u0026gt;/tmp/output.log 2\u0026gt;\u003cspan class=\"p\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"确认-cron-在跑\"\u003e确认 cron 在跑\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eps -ef \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep cron \u003cspan class=\"p\"\u003e|\u003c/span\u003e grep -v grep\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"路径要对\"\u003e路径要对\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003ecrontab 默认在 \u003ccode\u003e$HOME\u003c/code\u003e 下执行；若脚本里用 \u003ccode\u003eos.getcwd()\u003c/code\u003e，目录不对会报找不到模块。\u003c/li\u003e\n\u003cli\u003e命令里尽量写\u003cstrong\u003e绝对路径\u003c/strong\u003e，或在任务前 \u003ccode\u003esource ~/.zshrc\u003c/code\u003e，保证环境变量可用。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"最后一行要空行\"\u003e最后一行要空行\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e很多人踩坑：crontab 文件\u003cstrong\u003e末尾需要空行\u003c/strong\u003e，否则最后一条可能不执行。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"debianubuntu-的权限与命名\"\u003eDebian/Ubuntu 的权限与命名\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e/etc/cron.d\u003c/code\u003e 及 \u003ccode\u003ecron.{hourly,daily,weekly,monthly}\u003c/code\u003e 下的文件需满足：\u003c/p\u003e","title":"Crontab 排错笔记"},{"content":"1. 待办清单 从源码编译 nginx 如何调优内核与参数 nginx 如何处理请求 如何配置滚动日志 如何配置负载均衡 为什么要用 Lua 如何在 Lua 里强制 HTTPS 如何改写请求头 2. 从源码编译（步骤） （1）下载源码 从官网下载 nginx 源码包，并准备依赖：\n下载 PCRE（1.x 与 4.4 — 8.43）；按 nginx 文档，不支持 PCRE2。 下载 zlib（1.1.3 — 1.2.11）。 安装 OpenSSL：sudo apt-get install openssl-dev。 开始编译：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ./configure \\ --prefix=/opt/software/nginx/ \\ --with-http_stub_status_module \\ --with-http_sub_module \\ --with-http_gzip_static_module \\ --with-pcre=../pcre-8.42 \\ --with-zlib=../zlib-1.2.11 \\ --with-openssl=../openssl-1.1.1f \\ --with-http_secure_link_module \\ --with-http_random_index_module \\ --with-http_ssl_module \\ --with-http_realip_module \\ --with-http_addition_module \\ --with-http_gzip_static_module \\ --with-cc-opt=-O3 \\ --with-http_gunzip_module \\ --with-http_random_index_module \\ --with-http_secure_link_module \\ --with-http_auth_request_module \\ --with-threads \\ --with-stream_ssl_module \\ --with-http_slice_module \\ --with-file-aio \\ --with-http_v2_module \\ --without-mail_pop3_module \\ --without-mail_imap_module \\ --without-mail_smtp_module make \u0026amp;\u0026amp; make install （2）服务端参数调优 编辑 /etc/sysctl.conf，执行 /sbin/sysctl -p 生效。下面是部分 TCP/IP 与连接队列相关项：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # TCP/IP 相关 net.ipv4.tcp_max_tw_buckets = 6000 net.ipv4.ip_local_port_range = 1024 65000 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_syncookies = 1 #-------------------------------- net.core.somaxconn = 262144 net.core.netdev_max_backlog = 262144 #-------------------------------- net.ipv4.tcp_max_orphans = 262144 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_synack_retries = 1 net.ipv4.tcp_syn_reties = 1 net.ipv4.tcp_fin_timeout = 1 net.ipv4.tcp_keepalive_time = 30 （3）nginx 如何处理请求 nginx 由 core 与 module 组成。 模块分三类：核心模块、基础模块、第三方模块。 核心：http、event、mail 基础：http access、http fastcgi、http proxy、http rewrite 第三方：如 http upstream request hash、Notice、Http access key 等 请求路径概览： HTTP 请求 → nginx core → handlers → filter1 → filter2 → … → 输出 （4）OpenResty 模块列表（configure 选项摘录） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 --without-http_echo_module disable ngx_http_echo_module --without-http_xss_module disable ngx_http_xss_module --without-http_coolkit_module disable ngx_http_coolkit_module --without-http_set_misc_module disable ngx_http_set_misc_module --without-http_form_input_module disable ngx_http_form_input_module --without-http_encrypted_session_module disable ngx_http_encrypted_session_module --without-http_srcache_module disable ngx_http_srcache_module --without-http_lua_module disable ngx_http_lua_module --without-http_lua_upstream_module disable ngx_http_lua_upstream_module --without-http_headers_more_module disable ngx_http_headers_more_module --without-http_array_var_module disable ngx_http_array_var_module --without-http_memc_module disable ngx_http_memc_module --without-http_redis2_module disable ngx_http_redis2_module --without-http_redis_module disable ngx_http_redis_module --without-http_rds_json_module disable ngx_http_rds_json_module --without-http_rds_csv_module disable ngx_http_rds_csv_module --without-stream_lua_module disable ngx_stream_lua_module --without-ngx_devel_kit_module disable ngx_devel_kit_module --without-stream disable TCP/UDP proxy module --without-http_ssl_module disable ngx_http_ssl_module --without-stream_ssl_module disable ngx_stream_ssl_module --with-http_iconv_module enable ngx_http_iconv_module --with-http_drizzle_module enable ngx_http_drizzle_module --with-http_postgres_module enable ngx_http_postgres_module --without-lua_cjson disable the lua-cjson library --without-lua_tablepool disable the lua-tablepool library (and by consequence, the lua-resty-shell library) --without-lua_redis_parser disable the lua-redis-parser library --without-lua_rds_parser disable the lua-rds-parser library --without-lua_resty_dns disable the lua-resty-dns library --without-lua_resty_memcached disable the lua-resty-memcached library --without-lua_resty_redis disable the lua-resty-redis library --without-lua_resty_mysql disable the lua-resty-mysql library --without-lua_resty_upload disable the lua-resty-upload library --without-lua_resty_upstream_healthcheck disable the lua-resty-upstream-healthcheck library --without-lua_resty_string disable the lua-resty-string library --without-lua_resty_websocket disable the lua-resty-websocket library --without-lua_resty_limit_traffic disable the lua-resty-limit-traffic library --without-lua_resty_lock disable the lua-resty-lock library --without-lua_resty_lrucache disable the lua-resty-lrucache library --without-lua_resty_signal disable the lua-resty-signal library (and by consequence, the lua-resty-shell library) --without-lua_resty_shell disable the lua-resty-shell library --without-lua_resty_core disable the lua-resty-core library --with-luajit=DIR use the external LuaJIT 2.1 installation specified by DIR --with-luajit-xcflags=FLAGS Specify extra C compiler flags for LuaJIT 2.1 --with-luajit-ldflags=FLAGS Specify extra C linker flags for LuaJIT 2.1 --without-luajit-lua52 Turns off the LuaJIT extensions from Lua 5.2 that may break backward compatibility --without-luajit-gc64 Turns off the LuaJIT GC64 mode (which is enabled by default on x86_64) --with-libdrizzle=DIR specify the libdrizzle 1.0 (or drizzle) installation prefix --with-libpq=DIR specify the libpq (or postgresql) installation prefix --with-pg_config=PATH specify the path of the pg_config utility ","permalink":"https://yy-tech.online/zh/post/nginx-series-1/","summary":"\u003ch2 id=\"1-待办清单\"\u003e1. 待办清单\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e从源码编译 nginx\u003c/li\u003e\n\u003cli\u003e如何调优内核与参数\u003c/li\u003e\n\u003cli\u003enginx 如何处理请求\u003c/li\u003e\n\u003cli\u003e如何配置滚动日志\u003c/li\u003e\n\u003cli\u003e如何配置负载均衡\u003c/li\u003e\n\u003cli\u003e为什么要用 Lua\u003c/li\u003e\n\u003cli\u003e如何在 Lua 里强制 HTTPS\u003c/li\u003e\n\u003cli\u003e如何改写请求头\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"2-从源码编译步骤\"\u003e2. 从源码编译（步骤）\u003c/h2\u003e\n\u003ch3 id=\"1下载源码\"\u003e（1）下载源码\u003c/h3\u003e\n\u003cp\u003e从官网下载 nginx 源码包，并准备依赖：\u003c/p\u003e","title":"从源码编译 Nginx（1）"}]