当前位置: 首页 > Web前端 > vue.js

刨根问底,揭开 Vue 中 Scope CSS 实现的幕后(原理)

时间:2023-04-01 12:12:26 vue.js

.box{width:200px;楂樺害锛?00px锛涜儗鏅細#aff锛泒娣辩┒涓€涓嬶紝鎻紑Vue涓璖copeCSS瀹炵幇鐨勫箷鍚庯紙鍘熺悊锛夛紝CSS娣诲姞灞炴€ю煓冿笍锛熺‘瀹炴槸杩欐牱锛屼絾杩欏彧鏄渶缁圫copeCSS娓叉煋鐨勭粨鏋溿€傝€岃繖涓繃绋嬫槸濡備綍瀹炵幇鐨勫憿锛熸垜鎯宠兘鍥炵瓟绗竴鍜岀浜屼釜闂鐨勫悓瀛︿笉澶氥€傞偅涔堬紝鍥炲埌浠婂ぉ鐨勬枃绔狅紝鎴戝皢鍥寸粫浠ヤ笅3鐐癸紝浠嶸ue鐨凷copeCSS鐨勬渶缁堟覆鏌撶粨鏋滃叆鎵嬶紝娣卞叆娴呭嚭鍦拌瑙e叾瀹炵幇鐨勫簳灞傚師鐞嗭細浠€涔堟槸ScopeCSSvue-loader澶勭悊缁勪欢锛?vue鏂囦欢锛夎ˉ涓侀樁娈靛簲鐢⊿copeId鐢熸垚HTML灞炴€?浠€涔堟槸ScopeCSSScopeCSS鍗硈copeCSS锛岀粍浠跺寲涓嶅彲鍒嗗壊鐨勪竴閮ㄥ垎銆係copeCSS鍏佽鎴戜滑鍦ㄦ瘡涓粍浠朵腑瀹氫箟CSS鑰屼笉浼氶€犳垚姹℃煋銆傛瘮濡傛垜浠湪Vue涓畾涔変竴涓粍浠讹細.box{width:200px;楂樺害锛?00px锛涜儗鏅細#aff锛泒閫氬父鍦ㄥ紑鍙戠幆澧冧腑锛屾垜浠殑缁勪欢浼氬厛琚玽ue-loader澶勭悊锛岀劧鍚庣粨鍚堣繍琛屾鏋朵唬鐮佹覆鏌撳埌椤甸潰涓娿€傜浉搴斿湴锛屽畠浠搴旂殑HTML鍜孋SS濡備笅锛氶珮搴︼細200px锛沚ackground:#aff;}鍙互鐪嬪埌ScopeCSS鐨勬湰璐ㄦ槸鍩轰簬HTML鍜孋SS閫夋嫨鍣ㄧ殑灞炴€э紝鍒嗗埆閫氳繃缁橦TML鏍囩鍜孋SS閫夋嫨鍣ㄦ坊鍔燿ata-v-xxxx灞炴€ф潵瀹炵幇銆?vue-loader澶勭悊缁勪欢锛?vue鏂囦欢锛夊墠闈㈡垜浠篃鎻愬埌锛屽紑鍙戠幆澧冧腑鐨勪竴涓粍浠讹紙.vue鏂囦欢锛変細鍏堣vue-loader澶勭悊銆傜劧鍚庯紝瀵逛簬ScopeCSS锛寁ue-loader浼氬仛杩欎笁浠朵簨锛氳В鏋愮粍浠讹紝鎻愬彇template銆乻cript銆乻tyle瀵瑰簲鐨勪唬鐮佸潡缁撴瀯骞跺鍑篹xport缁勪欢瀹炰緥锛屽皢ScopId缁戝畾鍒皊tyle鐨勯€夐」涓婄粍浠跺疄渚婥SS浠g爜缂栬瘧杞崲锛孲copId鐢ㄤ簬鐢熸垚閫夋嫨鍣ㄧ殑灞炴€с€傛敞鎰忚繖閲屽彧鏄痸ue-loader瀵?vue鏂囦欢鐨勫鐞嗛儴鍒嗭紝涓嶆秹鍙奌MR鍜孌evtool鐨勯€昏緫銆傛湁鍏磋叮鐨勫悓瀛﹀彲浠ヨ嚜琛屽涔爚涓嶈繃锛寁ue-loader涔嬫墍浠ユ湁杩欎箞澶氱殑鑳藉姏锛屼富瑕佹槸鍥犱负vue-loader搴曞眰浣跨敤浜哣ue鎻愪緵鐨勫畼鏂瑰寘锛坧ackage锛堾vue/component-compiler-utils锛屽畠鎻愪緵瑙f瀽缁勪欢锛?vue鏂囦欢锛夈€佺紪璇慣emplate妯℃澘銆佺紪璇戞牱寮忕瓑涓夊ぇ鑳藉姏銆傞偅涔堬紝鎴戜滑鏉ョ湅鐪媣ue-loader鏄浣曚娇鐢ˊvue/component-compiler-utils瑙f瀽缁勪欢鏉ユ彁鍙栨ā鏉裤€佽剼鏈拰鏍峰紡鐨勩€?.1鎻愬彇妯℃澘銆佽剼鏈€乻tylevue-loader鎻愬彇妯℃澘銆佽剼鏈€佹牱寮忕殑杩囩▼涓昏浣跨敤@vue/component-compiler-utils鍖呯殑parse鏂规硶銆傝繖涓繃绋嬪搴旂殑浠g爜锛堜吉浠g爜锛変細鏄繖鏍风殑锛?/vue-loader/lib/index.jsconst{parse}=require("@vue/component-compiler-utils");module.exports=鍑芥暟锛堟潵婧愶級{constloaderContext=this;const{sourceMap,rootContext,resourcePath}=loaderContext;constsourceRoot=path.dirname(path.relative(context,resourcePath));constdescriptor=parse({source,compiler:require("vue-template-compiler"),filename,sourceRoot,needMap:sourceMap,});};涓嬮潰鎴戜滑閫愮偣鍒嗘瀽杩欐浠g爜銆傞鍏堜細鑾峰彇鍒板綋鍓嶇殑涓婁笅鏂噇oaderContext锛岄噷闈細鍖呭惈webpack鎵撳寘杩囩▼鐨勬牳蹇冨璞★紝姣斿compiler銆乧ompilation銆傚叾娆★紝鏋勫缓鏂囦欢璧勬簮鍏ュ彛sourceRoot锛屼竴鑸寚src鏂囦欢鐩綍锛屼富瑕佺敤浜庢瀯寤簊ource-map銆傛渶鍚庝細浣跨敤@vue/component-compiler-utils鎻愪緵鐨刾arse鏂规硶瑙f瀽婧愮爜锛堢粍浠朵唬鐮侊級銆傚湪杩欓噷锛屾垜浠厛鐪嬩竴涓媝arse鏂规硶鐨勫嚑涓弬鏁帮細source婧愪唬鐮佸潡锛岃繖閲屾槸缁勪欢瀵瑰簲鐨勪唬鐮侊紝閲岄潰鏈塼emplate锛宻tyle锛宻criptcompiler缂栬瘧鏍稿績瀵硅薄锛屾槸涓€涓狢ommonJS妯″潡锛坴ue-template-compiler)锛宲arse鏂规硶鍐呴儴浼氫娇鐢ㄥ叾鎻愪緵鐨刾arseComponent鏂规硶瑙f瀽componentfilename褰撳墠缁勪欢鐨勬枃浠跺悕锛屾瘮濡侫pp.vuesourceRoot鏂囦欢璧勬簮鍏ュ彛锛岀敤浜庢瀯寤簊ource-map鍒颁娇鐢ㄩ渶瑕佸湴鍥俱€倂alue(trueorfalse,榛樿涓簍rue)鍐冲畾鏄惁鐢熸垚script鍜宻tyle瀵瑰簲鐨剆ource-map锛宲arse鏂规硶鐨勬墽琛屼細杩斿洖涓€涓璞$粰desciptor锛岄噷闈細鍖呭惈template瀵瑰簲鐨勪唬鐮佸潡锛屾牱寮忓拰鑴氭湰鍒嗗埆銆傜劧鍚庯紝澶у鐪嬪埌鐨勬槸锛寁ue-loader瑙f瀽缁勪欢鐨勮繃绋嬪嚑涔庡鍖呯粰浜哣ue鎻愪緵鐨勫伐鍏峰寘锛坧ackage锛夈€傝€屼笖锛屾垜鎯宠繖鏃跺€欒偗瀹氭湁鍚屽浼氶棶浜嗭細杩欏拰Vue鐨凷copeCSS鏈変粈涔堝叧绯火煓冿笍锛熸湁寰堝ぇ鐨勫叧绯伙紒鍥犱负Vue鐨凷copeCSS涓嶆槸鏃犵背涔嬬倞锛屽畠瀹炵幇鐨勫墠鎻愭槸瑙f瀽缁勪欢锛岀劧鍚庡垎鍒鐞嗘ā鏉垮拰鏍峰紡閮ㄥ垎鐨勪唬鐮侊紒濂戒簡锛屾樉鐒跺埌杩欓噷鎴戜滑灏卞畬鎴愪簡缁勪欢鐨勮В鏋愪簡銆傛帴涓嬫潵闇€瑕佹瀯寤哄苟瀵煎嚭缁勪欢瀹炰緥~2.2鏋勫缓骞跺鍑虹粍浠跺疄渚嬨€倂ue-loader瑙f瀽瀹岀粍浠跺悗锛屼細鍒嗗埆澶勭悊鐢熸垚妯℃澘銆佽剼鏈€佹牱寮忕殑import璇彞锛岀劧鍚庤皟鐢╪ormalizer鏂规硶瀵圭粍浠惰繘琛岃鑼冨寲锛坣ormalizer锛夛紝鏈€鍚庢嫾鎺ユ垚浠g爜涓诧細lettemplateImport=`varrender,staticRenderFns`;if(descriptor.template){//鏋勯€犳ā鏉跨殑瀵煎叆璇彞}letscriptImport=`varscript={}`;script){//鏋勯€爏cript鐨勫鍏ヨ鍙letstylesCode=``;if(descriptor.styles.length){//鏋勯€爏tyle鐨勫鍏ヨ鍙letcode=`${templateImport}${scriptImport}${stylesCode}浠?{stringifyRequest(`!${componentNormalizerPath}`)}varcomponent=normalizer(script,render,staticRenderFns,${hasFunctional?`true`:`false`},${/injectStyles/.test(stylesCode)?`injectStyles`:`null`},${hasScoped?JSON.stringify(id):`null`},${isServer?JSON.stringify(hash(request)):`null`}${isShadow?`,true`:``})`.trim()+`\n`;鍏朵腑锛宼emplateImport銆乻criptImport銆乻tylesCode绛夋瀯閫犵殑妯℃澘銆佽剼鏈€佹牱寮忛儴鍒嗙殑import璇彞浼氭槸杩欐牱鐨勶細import{render,staticRenderFns,}from"./App.vue?vue&type=template&id=7ba5bd90&scoped=true&";importscriptfrom"./App.vue?vue&type=script&lang=js&";//Exportexport*from"./App.vue?vue&type=script&lang=js&";浠?./App.vue?vue&type=style&index=0&id=7ba5bd90&scoped=true&lang=css&"瀵煎叆style0;涓嶇煡閬撳悓瀛︿滑鏈夋病鏈夋敞鎰忊殸锔忥紝template鍜宻tyle鐨刬mport璇彞閮芥湁杩欎箞涓€涓叕鍏遍儴鍒唅d=7ba5bd90&scoped=true锛屼篃灏辨槸璇存鏃剁粍浠剁殑template鍜宻tyle闇€瑕丼copeCSS锛屼互鍙妔copeId鏄?ba5bd90褰撶劧杩欏彧鏄涓€姝ワ紝鍛婅瘔鍚庨潰鐨勬ā鏉垮拰鏍峰紡缂栬瘧闇€瑕佹敞鎰忕敓鎴怱copeCSS锛岃繖鏄疭copeCSS鐨勭涓€姝ワ紒鐒跺悗浼氳皟鐢╪ormalizer鏂规硶瀵圭粍浠惰繘琛岃鑼冨寲锛圢ormalizer锛夛細importnormalizerfrom"!../node_modules/vue-loader/lib/runtime/componentNormalizer.js";varcomponent=normalizer(script,render,staticRenderFns,false,null,"7ba5bd90",null);瀵煎嚭榛樿缁勪欢.exports锛涙敞鎰弉ormalizer鏄鍘熸柟娉昻ormalizeComponent鐨勯噸鍛藉悕锛屼互涓嬬粺绉颁负normalizeComponent~鎴戞兂鍚屽浠簲璇ユ敞鎰忓埌浜嗭紝姝ゆ椂scopeId浼氫綔涓哄弬鏁颁紶閫掔粰normalizeComponent鏂规硶锛屼紶閫掔殑鐩殑鏄痭ormalizeComponent鏄皢scopeId缁戝畾鍒扮粍浠跺疄渚嬬殑閫夐」涓娿€傞偅涔堬紝璁╂垜浠湅涓€涓媙ormalizeComponent鏂规硶锛堜吉浠g爜锛夛細.varoptions=typeofscriptExports==='function'?scriptExports.options:scriptExports//scopedIdif(scopeId){options._scopeId='data-v-'+scopeId}...}濡傛偍鎵€瑙侊紝杩欓噷鐨勯€夐」銆俖scopeId浼氱瓑浜巇ata-v-7ba5bd90锛屽畠鐨勪綔鐢ㄤ富瑕佹槸鍦ㄦ墦琛ヤ竵鐨勬椂鍊欑粰褰撳墠缁勪欢鐨凥TML鏍囩娣诲姞涓€涓悕涓篸ata-v-7ba5bd90鐨勫睘鎬с€傛墍浠ヨ繖灏辨槸妯℃澘鐢眘copeId缁勬垚鐨勭湡姝e師鍥狅紒2.3缂栬瘧鏍峰紡Style锛屽簲鐢⊿copId鐢熸垚selector灞炴€ф瀯閫犲畬Style瀵瑰簲鐨刬mport璇彞鍚庯紝鐢变簬姝ゆ椂import璇彞涓殑query鍖呭惈vue锛屾墍浠ヤ細琚玽ue-loader鍐呴儴鐨凱itchingLoader澶勭悊銆侾itchingLoader浼氶噸鍐檌mport璇彞锛屾嫾鎺ュ唴鑱旓紙inline锛塋oader锛岀湅璧锋潵鍍忚繖鏍凤細export*from'"-!../node_modules/vue-style-loader/index.js??ref--6-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--6-oneOf-1-2!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&id=7ba5bd90&scoped=true&lang=css&"'鐒跺悗webpack浼氳В鏋愰渶瑕佺殑Loader鐢辨ā鍧椼€傚緢鏄庢樉锛岃繖閲屼細瑙f瀽6涓狶oader锛歔{loader:"vue-style-loader",options:"?ref--6-oneOf-1-0"},{loader:"css-loader",options:"?ref--6-oneOf-1-1"},{loader:"stylePostLoader",options:undefined},{loader:"postcss-loader",options:"?ref--6-oneOf-1-2"},{loader:"cache-loader",options:"?ref--0-0"},{loader:"vue-loader",options:"?vue-loader-options"}]鐒跺悗锛岃繖涓椂鍊欙紝webpack浼氭墽琛岃繖6涓狶oader锛堝綋鐒惰繕鏈夎В鏋愭ā鍧楁湰韬級銆傝€屼笖webpack.config.js涓鍚堣鍒欑殑NormalLoader浼氬湪杩欓噷琚拷鐣ワ紙vue-style-loader涔熶細蹇界暐鍓嶇鐨凩oader锛夈€備笉浜嗚ВinlineLoader鐨勫悓瀛﹀彲浠ョ湅鐪嬭繖绡囥€恮ebpack杩涢樁銆戜綘鐪熺殑鎺屾彙浜唋oader鍚楋紵-Loader鍗侀棶ScopeCSS锛屾牳蹇冩槸stylePostLoader銆傛帴涓嬫潵鎴戜滑鐪嬩竴涓媠tylePostLoader鐨勫畾涔夛細const{compileStyle}=require("@vue/component-compiler-utils");module.exports=function(source,inMap){constquery=qs.parse(this.resourceQuery.slice(1));const{code,map,errors}=compileStyle({source,filename:this.resourcePath,id:`data-v-${query.id}`,map:inMap,scoped:!!query.scoped,trim:true,});濡傛灉(errors.length){this.callback(errors[0]);}else{this.callback(null,code,map);}};浠巗tylePostLoader鐨勫畾涔夋垜浠煡閬擄紝瀹冧娇鐢ˊvue/component-compiler-utils鎻愪緵鐨刢ompileStyle鏂规硶鏉ュ畬鎴愮粍浠舵牱寮忕殑缂栬瘧銆傚苟涓旓紝姝ゆ椂鍙傛暟id浼氫綔涓篸ata-v-${query.id}浼犲叆锛屽嵆data-v-7ba5bd90锛岃繖涔熸槸selector鐨勫睘鎬у0鏄庡湪鏍峰紡鏄痵copeId锛佸湪compileStyle鍑芥暟鍐呴儴锛屼娇鐢ㄤ簡澶у鐔熺煡鐨刾ostcss鏉ョ紪璇戞牱寮忎唬鐮侊紝鏋勯€犻€夋嫨鍣ㄧ殑scopeId灞炴€с€傝嚦浜庡浣曚娇鐢╬ostcss鏉ュ畬鎴愯繖涓繃绋嬶紝杩欓噷灏变笉杩囧浠嬬粛浜嗐€傛湁鍏磋叮鐨勫悓瀛﹀彲浠ヨ嚜琛屽涔爚3Patch闃舵搴旂敤ScopeId鐢熸垚HTML灞炴€с€備笉鐭ラ亾澶у杩樿涓嶈寰?.2涓瀯閫犲拰瀵煎嚭缁勪欢瀹炰緥鐨勬椂鍊欍€傛垜浠杩囧湪缁勪欢瀹炰緥鐨刼ptions涓婄粦瀹歘scopeId鏄疄鐜版ā鏉縎cope鐨勫叧閿偣锛佷絾鏄紝鎴戜滑骞舵病鏈変粙缁嶈繖涓猒scopeId鏄浣曞簲鐢ㄥ埌妯℃澘涓婄殑鍏冪礌涓婄殑馃槻锛熷鏋滀綘鎯冲湪vue-loader鎴栬€匑vue/component-compiler-utils鐨勪唬鐮佷腑鎵惧埌杩欎釜绛旀锛屾垜鍙互鍛婅瘔浣狅紝涓€涓囧勾閮芥壘涓嶅埌锛佸洜涓猴紝瀹為檯搴旂敤_scopeId鐨勮繃绋嬪彂鐢熷湪Vue杩愯鏃剁殑妗嗘灦浠g爜涓紙娌℃兂鍒梆煒碉級銆備簡瑙h繃Vue妯℃澘缂栬瘧杩囩▼鐨勫悓瀛︼紝鎴戞兂搴旇閮界煡閬撴ā鏉夸細琚紪璇戞垚render鍑芥暟锛岀劧鍚庢牴鎹畆ender鍑芥暟鍒涘缓瀵瑰簲鐨刅Node锛屾渶鍚庡皢VNode娓叉煋鎴愰〉闈笂鐨勭湡瀹濪OM锛氫互鍙奦Node鍒扮湡瀹濪OM杩欎釜杩囩▼鏄€氳繃patch鏂规硶瀹屾垚鐨勩€傚亣璁撅紝姝ゆ椂鎴戜滑鏄涓€娆℃覆鏌揇OM锛屼細鍛戒腑patch鏂规硶涓璱sUndef(oldVnode)涓簍rue鐨勯€昏緫锛?{//绌烘寕杞斤紙鍙兘鏄粍浠讹級锛屽垱寤烘柊鐨勬牴鍏冪礌isInitialPatch=truecreateElm(vnode,insertedVnodeQueue)}}鍥犱负DOM鏄涓€娆℃覆鏌擄紝鏍规湰娌℃湁oldVnode馃樁浣犲彲浠ョ湅鍒癱reateElm杩欎釜鏃跺€欎細鎵ц鏂规硶銆傚湪createElm鏂规硶涓紝浼氬垱寤篤Node瀵瑰簲鐨勭湡瀹濪OM锛岃€屼笖杩樺仛浜嗕竴浠跺緢閲嶈鐨勪簨鎯咃紝璋冪敤setScope鏂规硶搴旂敤_scopeId鍦―OM涓婄敓鎴恉ata-v-xxx灞炴€э紒瀵瑰簲浠g爜锛堜吉浠g爜锛夛細//packages/src/core/vdom/patch.jsfunctioncreateElm(vnode,insertedVnodeQueue,parentElm,refElm,nested,ownerArray,index){...setScope(vnode);...}鍦╯etScope鏂规硶涓紝缁勪欢瀹炰緥鐨刼ptions._scopeId浼氫綔涓轰竴涓睘鎬ф坊鍔犲埌DOM涓紝浠庤€屽湪妯℃澘涓殑HTML鏍囩涓婄敓鎴愪竴涓悕涓篸ata-v-xxx鐨勫睘鎬с€傝€屼笖杩欎釜杩囩▼浼氶€氳繃Vue灏佽鐨勫伐鍏峰嚱鏁皀odeOps.setStyleScope鏉ュ畬鎴愩€傚畠鐨勬湰璐ㄦ槸璋冪敤DOM瀵硅薄鐨剆etAttribute鏂规硶锛?/src/platforms/web/runtime/node-ops.jsexportfunctionsetStyleScope(node:Element,scopeId:string){node.setAttribute(scopeId,'')}缁撹濡傛灉浣犲湪缃戜笂鎵捐繃鍏充簬vue-loader鍜孲copeCSS鐨勬枃绔狅紝浣犱細鍙戠幇寰堝鏂囩珷閮芥槸鍦ˊvue/component-compiler-utils鍖呯殑compilerTemplate鏂规硶涓娇鐢╯copeId鐢熸垚灞炴€х殑妯℃澘涓殑HTML鏍囪銆備絾鏄紝閫氳繃闃呰杩欑瘒鏂囩珷锛屾垜浠細鍙戠幇涓よ€呮鏃犲叧绯伙紙SSR鐨勬儏鍐甸櫎澶栵級锛佸苟涓旓紝鎴戞兂鍚屽浠篃娉ㄦ剰鍒颁簡锛屾湰鏂囨彁鍒扮殑Vue杩愯鏃舵鏋剁殑浠g爜鏄疺ue2.x鐗堟湰锛堜笉鏄疺ue3锛夈€傛墍浠ユ湁鍏磋叮鐨勫悓瀛﹀彲浠ラ€氳繃鏈枃鎻愪緵鐨勮矾绾垮幓鐞㈢(涓€涓媀ue3涓璖copeCSS鐨勮繃绋嬶紝鐩镐俊浣犱細鏀惰幏寰堝馃槑銆傛渶鍚庯紝濡傛灉鏈枃鏈変换浣曚笉褰撴垨鑰呰〃杈鹃敊璇殑鍦版柟锛屾杩庡悇浣嶅悓瀛︽彁浜ssue~鐐逛釜璧烉煈嶇湅瀹岃繖绡囨枃绔狅紝濡傛灉鏈夋敹鑾凤紝鐐逛釜璧炲惂锛岃繖浼氭垚涓烘垜缁х画鐨勫姩鍔涘垎浜紝璋㈣阿~鎴戞槸浜斿叚锛屽枩娆㈠垱鏂板拰鎹i紦婧愮爜锛屼笓娉ㄤ簬婧愮爜锛圴ue3銆乂ite锛夈€佸墠绔伐绋嬨€佽法绔瓑鎶€鏈涔犲拰鍒嗕韩銆傚彟澶栵紝鎴戞墍鏈夌殑鏂囩珷閮戒細鏀跺綍鍦╤ttps://github.com/WJCHumble/Blog锛屾杩庡叧娉ㄦ垨Star锛?/p>

最新推荐
猜你喜欢