[{"data":1,"prerenderedAt":594},["ShallowReactive",2],{"page-\u002Fvue\u002Fvue的tree-shaking算法":3},{"id":4,"title":5,"body":6,"description":127,"extension":588,"meta":589,"navigation":308,"path":590,"seo":591,"stem":592,"__hash__":593},"content\u002FVue\u002Fvue的tree-shaking算法.md","Vue的tree Shaking算法",{"type":7,"value":8,"toc":578},"minimark",[9,14,26,38,42,55,65,69,72,87,94,97,100,103,113,116,121,280,285,329,336,339,342,352,357,363,366,387,392,397,409,460,463,468,471,476,481,574],[10,11,13],"h3",{"id":12},"tree-shaking-的起源","Tree-Shaking 的起源",[15,16,17,18,25],"p",{},"​\tTree-Shaking 起源于 ",[19,20,24],"a",{"href":21,"rel":22},"https:\u002F\u002Fzhida.zhihu.com\u002Fsearch?content_id=247751425&content_type=Article&match_order=1&q=ES6+%E6%A8%A1%E5%9D%97%E7%B3%BB%E7%BB%9F&zhida_source=entity",[23],"nofollow","ES6 模块系统","。在 ES6 模块中，每个模块都有一个明确的入口点，即模块的顶层作用域。当模块被导入时，顶层作用域中的所有变量和函数都会被导入。",[15,27,28,29,33,34,37],{},"​\t然而，",[30,31,32],"strong",{},"有些变量和函数可能没有被使用到，这就导致了代码的冗余","。Tree-Shaking 的目的是通过分析模块之间的依赖关系，",[30,35,36],{},"找出那些没有被使用的代码，并将其从最终的打包文件中移除","。",[10,39,41],{"id":40},"tree-shaking-的原理","Tree-Shaking 的原理",[15,43,44,45,50,51,54],{},"​\tTree-Shaking 的原理是基于",[19,46,49],{"href":47,"rel":48},"https:\u002F\u002Fzhida.zhihu.com\u002Fsearch?content_id=247751425&content_type=Article&match_order=1&q=%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90&zhida_source=entity",[23],"静态分析","。在打包过程中，",[30,52,53],{},"打包工具会遍历所有的模块，并记录下每个模块的导出和导入关系。然后，通过分析这些关系，找出那些没有被使用的模块和代码","。最后，将这些无用代码从打包文件中移除。",[15,56,57,58,37,61,64],{},"​\t具体来说，Tree-Shaking 的核心思想是“只打包需要的代码”。",[30,59,60],{},"当一个模块被导入时，打包工具会检查该模块中的所有导出变量和函数",[30,62,63],{},"如果这些变量和函数没有被其他模块导入，那么它们就不会被打包到最终的文件中","。这样，就可以有效地去除冗余代码，减少文件大小。",[10,66,68],{"id":67},"tree-shaking-技术实现","Tree-Shaking 技术实现",[10,70,71],{"id":71},"打包工具支持",[15,73,74,75,80,81,86],{},"要实现 Tree-Shaking，首先需要使用支持 Tree-Shaking 的打包工具。目前主流的前端打包工具如 ",[19,76,79],{"href":77,"rel":78},"https:\u002F\u002Fzhida.zhihu.com\u002Fsearch?content_id=247751425&content_type=Article&match_order=1&q=Webpack&zhida_source=entity",[23],"Webpack","、",[19,82,85],{"href":83,"rel":84},"https:\u002F\u002Fzhida.zhihu.com\u002Fsearch?content_id=247751425&content_type=Article&match_order=1&q=Rollup&zhida_source=entity",[23],"Rollup"," 等都支持 Tree-Shaking。以 Webpack 为例**，可以通过在 Webpack 配置文件中设置 mode 为 production 来启用 Tree-Shaking。**",[10,88,90],{"id":89},"代码拆分",[19,91,89],{"href":92,"rel":93},"https:\u002F\u002Fzhida.zhihu.com\u002Fsearch?content_id=247751425&content_type=Article&match_order=1&q=%E4%BB%A3%E7%A0%81%E6%8B%86%E5%88%86&zhida_source=entity",[23],[15,95,96],{},"在 Tree-Shaking 中，代码拆分是一种重要的优化策略。通过将代码拆分成更小的模块，可以减少冗余代码的数量，并提高 Tree-Shaking 的效率。",[10,98,99],{"id":99},"摇树优化注意事项",[15,101,102],{},"Tree Shaking 只支持 ESM 的引入方式，不支持 CommonJS 引入方式",[104,105,106,110],"ul",{},[107,108,109],"li",{},"ESM: export + import",[107,111,112],{},"CommonJS: module.exports + require",[10,114,115],{"id":115},"定义三种类型的方法",[104,117,118],{},[107,119,120],{},"func.js 定义函数",[122,123,128],"pre",{"className":124,"code":125,"language":126,"meta":127,"style":127},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F 引用并被调用的方法\nexport function quoteAndUse() {\n  console.log(\"这是use专属的方法，别人都\");\n  return;\n  console.log(\"这是use中return后面的\");\n}\n\u002F\u002F 引用但是未被调用的方法\nexport function quoteButNotUse() {\n  console.log(\"这是quoteButNotUse方法\");\n}\n\u002F\u002F 未被引用的方法\nexport function notQuote() {\n  console.log(\"这是未被引用的方法\");\n}\n","js","",[129,130,131,140,158,177,186,200,206,212,224,238,243,249,261,275],"code",{"__ignoreMap":127},[132,133,136],"span",{"class":134,"line":135},"line",1,[132,137,139],{"class":138},"sJ8bj","\u002F\u002F 引用并被调用的方法\n",[132,141,143,147,150,154],{"class":134,"line":142},2,[132,144,146],{"class":145},"szBVR","export",[132,148,149],{"class":145}," function",[132,151,153],{"class":152},"sScJk"," quoteAndUse",[132,155,157],{"class":156},"sVt8B","() {\n",[132,159,161,164,167,170,174],{"class":134,"line":160},3,[132,162,163],{"class":156},"  console.",[132,165,166],{"class":152},"log",[132,168,169],{"class":156},"(",[132,171,173],{"class":172},"sZZnC","\"这是use专属的方法，别人都\"",[132,175,176],{"class":156},");\n",[132,178,180,183],{"class":134,"line":179},4,[132,181,182],{"class":145},"  return",[132,184,185],{"class":156},";\n",[132,187,189,191,193,195,198],{"class":134,"line":188},5,[132,190,163],{"class":156},[132,192,166],{"class":152},[132,194,169],{"class":156},[132,196,197],{"class":172},"\"这是use中return后面的\"",[132,199,176],{"class":156},[132,201,203],{"class":134,"line":202},6,[132,204,205],{"class":156},"}\n",[132,207,209],{"class":134,"line":208},7,[132,210,211],{"class":138},"\u002F\u002F 引用但是未被调用的方法\n",[132,213,215,217,219,222],{"class":134,"line":214},8,[132,216,146],{"class":145},[132,218,149],{"class":145},[132,220,221],{"class":152}," quoteButNotUse",[132,223,157],{"class":156},[132,225,227,229,231,233,236],{"class":134,"line":226},9,[132,228,163],{"class":156},[132,230,166],{"class":152},[132,232,169],{"class":156},[132,234,235],{"class":172},"\"这是quoteButNotUse方法\"",[132,237,176],{"class":156},[132,239,241],{"class":134,"line":240},10,[132,242,205],{"class":156},[132,244,246],{"class":134,"line":245},11,[132,247,248],{"class":138},"\u002F\u002F 未被引用的方法\n",[132,250,252,254,256,259],{"class":134,"line":251},12,[132,253,146],{"class":145},[132,255,149],{"class":145},[132,257,258],{"class":152}," notQuote",[132,260,157],{"class":156},[132,262,264,266,268,270,273],{"class":134,"line":263},13,[132,265,163],{"class":156},[132,267,166],{"class":152},[132,269,169],{"class":156},[132,271,272],{"class":172},"\"这是未被引用的方法\"",[132,274,176],{"class":156},[132,276,278],{"class":134,"line":277},14,[132,279,205],{"class":156},[104,281,282],{},[107,283,284],{},"在页面中不同场景进行使用 notQuote 未被引用的方法 quoteButNotUse 引用但是未被调用的方法 quoteAndUse 引用却已被调用的方法",[122,286,288],{"className":124,"code":287,"language":126,"meta":127,"style":127},"import { quoteButNotUse, quoteAndUse } from '.\u002Ffunc'\n\ntestUse() {\n  quoteAndUse()\n}\n",[129,289,290,304,310,317,325],{"__ignoreMap":127},[132,291,292,295,298,301],{"class":134,"line":135},[132,293,294],{"class":145},"import",[132,296,297],{"class":156}," { quoteButNotUse, quoteAndUse } ",[132,299,300],{"class":145},"from",[132,302,303],{"class":172}," '.\u002Ffunc'\n",[132,305,306],{"class":134,"line":142},[132,307,309],{"emptyLinePlaceholder":308},true,"\n",[132,311,312,315],{"class":134,"line":160},[132,313,314],{"class":152},"testUse",[132,316,157],{"class":156},[132,318,319,322],{"class":134,"line":179},[132,320,321],{"class":152},"  quoteAndUse",[132,323,324],{"class":156},"()\n",[132,326,327],{"class":134,"line":188},[132,328,205],{"class":156},[15,330,331,332,335],{},"可以设置",[30,333,334],{},"生产环境生效 dev 环境打包","： 打包结果发现，所有的代码都被打包了，三种方法均存在，但是下面两个未被引用或使用的方法被标注了 unused harmony export（声明未使用）",[15,337,338],{},"prod 环境打包： 无法到达的代码（例如 return 后面的代码）、引用未被使用的方法、 未被引用的方法、注释信息， 都不会被打包到 bundle.js 中",[10,340,341],{"id":341},"摇树优化副作用",[15,343,344,347,348,351],{},[30,345,346],{},"sideEffects 指的是有副作用的代码，假如模块 A 中包含一些影响全局作用域（非模块作用域）的代码","，如果改变了全局的变量或对象的变量，",[30,349,350],{},"模块 B 引入了模块 A，但并未使用。在这种情况下，模块 A 就被认为是有副作用的，webpack 是不会删除模块 A 中所有未使用的代码的","，它还会保留模块 A 中立即执行并对全局环境有影响的代码。",[104,353,354],{},[107,355,356],{},"新建 about.js 文件： 编写 about 方法，挂载到全局，在 main.js 中引入，但未真正调用",[15,358,359],{},[360,361],"img",{"alt":360,"src":362},".\u002Fassets\u002Fv2-10d1031dcb4e5968e18456cc9d917251_1440w.jpg",[15,364,365],{},"在 main.ts 中引入,未使用 about 方法",[122,367,371],{"className":368,"code":369,"language":370,"meta":127,"style":127},"language-ts shiki shiki-themes github-light github-dark","import { about } from \".\u002Fabout.js\";\n","ts",[129,372,373],{"__ignoreMap":127},[132,374,375,377,380,382,385],{"class":134,"line":135},[132,376,294],{"class":145},[132,378,379],{"class":156}," { about } ",[132,381,300],{"class":145},[132,383,384],{"class":172}," \".\u002Fabout.js\"",[132,386,185],{"class":156},[104,388,389],{},[107,390,391],{},"打包后：我们会发现 about 会被打包在 bundle.js 中",[15,393,394],{},[360,395],{"alt":360,"src":396},".\u002Fassets\u002Fv2-7d3d00cd185345f21eb093a7f3164867_1440w.jpg",[104,398,399,406],{},[107,400,401,402,405],{},"如果",[30,403,404],{},"设置了\"sideEffects\": false，看下面总结说明 请注意： 任何导入的文件都会收到树抖动的影响","。如果 css-loader 在项目中使用类似的东西并导入 css 文件，则需要将其添加到副作用列表中，这样它就不会在生产模式下无意中被删除。 如果有某些模块是有副作用的，那么可以将它的路径加入 package.json 的 sideEffects 数组选项中，这样也能提高删除未使用代码的速度。",[107,407,408],{},"通过以上描述，我们可以把样式文件和我们最开始的 about 文件添加在副作用数组中",[122,410,414],{"className":411,"code":412,"language":413,"meta":127,"style":127},"language-json shiki shiki-themes github-light github-dark","{\n  \"name\": \"your-project\",\n   \"sideEffects\": [\"about.js\", \"*.css\"],\n}\n","json",[129,415,416,421,436,456],{"__ignoreMap":127},[132,417,418],{"class":134,"line":135},[132,419,420],{"class":156},"{\n",[132,422,423,427,430,433],{"class":134,"line":142},[132,424,426],{"class":425},"sj4cs","  \"name\"",[132,428,429],{"class":156},": ",[132,431,432],{"class":172},"\"your-project\"",[132,434,435],{"class":156},",\n",[132,437,438,441,444,447,450,453],{"class":134,"line":160},[132,439,440],{"class":425},"   \"sideEffects\"",[132,442,443],{"class":156},": [",[132,445,446],{"class":172},"\"about.js\"",[132,448,449],{"class":156},", ",[132,451,452],{"class":172},"\"*.css\"",[132,454,455],{"class":156},"],\n",[132,457,458],{"class":134,"line":179},[132,459,205],{"class":156},[15,461,462],{},"使用示例对比： 新建 index.css 文件，用于编写样式",[15,464,465],{},[360,466],{"alt":360,"src":467},".\u002Fassets\u002Fv2-cc4f7099ccbd0f1e697935bbf0c82d9f_1440w.jpg",[15,469,470],{},"在 main.js 中进行引入",[15,472,473],{},[360,474],{"alt":360,"src":475},".\u002Fassets\u002Fv2-3fc15d652ebabd4d5362fb2a1abd5089_1440w.jpg",[15,477,478],{},[30,479,480],{},"总结",[122,482,484],{"className":124,"code":483,"language":126,"meta":127,"style":127},"\u002F\u002F 所有文件都有副作用，全都不可 tree-shaking\n{\n \"sideEffects\": true\n}\n\n\u002F\u002F 没有文件有副作用，全都可以 tree-shaking，即告知 webpack，它可以安全地删除未用到的 export。\n{\n \"sideEffects\": false\n}\n\n\u002F\u002F 除了数组中包含的文件外有副作用，所有其他文件都可以 tree-shaking，但会保留符合数组中条件的文件\n{\n \"sideEffects\": [\n   \"*.css\",\n   \"*.less\"\n ]\n",[129,485,486,491,495,505,509,513,518,522,531,535,539,544,548,555,562,568],{"__ignoreMap":127},[132,487,488],{"class":134,"line":135},[132,489,490],{"class":138},"\u002F\u002F 所有文件都有副作用，全都不可 tree-shaking\n",[132,492,493],{"class":134,"line":142},[132,494,420],{"class":156},[132,496,497,500,502],{"class":134,"line":160},[132,498,499],{"class":172}," \"sideEffects\"",[132,501,429],{"class":156},[132,503,504],{"class":425},"true\n",[132,506,507],{"class":134,"line":179},[132,508,205],{"class":156},[132,510,511],{"class":134,"line":188},[132,512,309],{"emptyLinePlaceholder":308},[132,514,515],{"class":134,"line":202},[132,516,517],{"class":138},"\u002F\u002F 没有文件有副作用，全都可以 tree-shaking，即告知 webpack，它可以安全地删除未用到的 export。\n",[132,519,520],{"class":134,"line":208},[132,521,420],{"class":156},[132,523,524,526,528],{"class":134,"line":214},[132,525,499],{"class":172},[132,527,429],{"class":156},[132,529,530],{"class":425},"false\n",[132,532,533],{"class":134,"line":226},[132,534,205],{"class":156},[132,536,537],{"class":134,"line":240},[132,538,309],{"emptyLinePlaceholder":308},[132,540,541],{"class":134,"line":245},[132,542,543],{"class":138},"\u002F\u002F 除了数组中包含的文件外有副作用，所有其他文件都可以 tree-shaking，但会保留符合数组中条件的文件\n",[132,545,546],{"class":134,"line":251},[132,547,420],{"class":156},[132,549,550,552],{"class":134,"line":263},[132,551,499],{"class":172},[132,553,554],{"class":156},": [\n",[132,556,557,560],{"class":134,"line":277},[132,558,559],{"class":172},"   \"*.css\"",[132,561,435],{"class":156},[132,563,565],{"class":134,"line":564},15,[132,566,567],{"class":172},"   \"*.less\"\n",[132,569,571],{"class":134,"line":570},16,[132,572,573],{"class":156}," ]\n",[575,576,577],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":127,"searchDepth":142,"depth":142,"links":579},[580,581,582,583,584,585,586,587],{"id":12,"depth":160,"text":13},{"id":40,"depth":160,"text":41},{"id":67,"depth":160,"text":68},{"id":71,"depth":160,"text":71},{"id":89,"depth":160,"text":89},{"id":99,"depth":160,"text":99},{"id":115,"depth":160,"text":115},{"id":341,"depth":160,"text":341},"md",{},"\u002Fvue\u002Fvue的tree-shaking算法",{"description":127},"Vue\u002Fvue的tree-shaking算法","JifSwTT4aKMipH_FZsEIvQTemC058abjiYornKma7yU",1791042471202]