Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
文以字節跳動的微前端項目運行時隔離任務出發,深入講解 sandbox (后稱沙盒)的技術實現,以及上述實際實現過程中一些發生過的問題。把一些關鍵細節和多年的采坑經驗充分的分享給讀者,希望能對大家有一些幫助和啟發。至于對微前端的整體思考,可以參考:《前端微服務在字節跳動的打磨與應用》,希望讀者在讀本文之前已經有所了解。
首先從沙盒對微前端乃至前端的意義開始講起。沙盒對軟件工程來說其概念不算是新鮮事物,僅看前端對隔離的需求也由來已久。并且根據不同的實際業務場景,已經有過非常多和有特色的探索。
一切都從 iframe 開始講起了,反正是個看上去很美的解決方案。沒有真的用過的人肯定都會這樣想象。但有些可能等到你要真的搞一個通過 iframe 全面聚合的才知道。單純的 iframe 聚合非常麻煩,需要很多補漏的勞動量。
舊的 iframe 的方案可以在一定程度上解決了耦合問題。具體是把一個站點頁面拆成 N 個 frame,每個 frame 單獨跑一個獨立的域名。
它的好處非常清楚,獨立上下,獨立運行,誰都不會妨礙誰。但是是不是這樣沙盒就完成了呢?這樣算不算沙盒就不好說了,會有很多不同的觀點和理論。比如一些觀點可能會覺得沙盒不是像這樣全獨立,而是以隔離模擬獨立。后面我們會再次談到這個觀點,并從實現角度分享一些我們的思考成果。
因為一個完整的項目包含大量公用的功能和代碼,例如登錄身份、站內信,業務模塊只是其中的一個部分。這部分完全用跨 window 通信實現起來很費時費力,并且單頁應用了 React 或類似的加載技術展示之后,iframe 的效果也遜色很多。想要突破這些限制,困難就很多了。
第一點不用多說各位都會想到 deeplinking 的問題,對吧,至少這點得做到才能算是一個工程,尤其 MVC 時代以來路由一直非常重要。
還有就是各種共享的東西,比如登錄怎么共享。iframe 當然也不是不行。和前面后面提到的諸多問題一樣都不是不行、而是很麻煩,要針對他去解決很多困難。從效果上講,最終也完全可以形成一個不錯的 iframe 沙盒。
另一顯然的困難是,組件庫、組件風格的父子傳遞,以及 React VUE 等渲染引擎的底層代碼、內存對象的傳遞。初步的實現是加入分片打包功能,拆 common chunk 并獨立部署 CDN 上,最后在加載時,通過瀏覽器自身的緩存能力加速訪問。但是運行時內存并不共享,對包的運行時修改也難以復用。
還有數據層的設計,數據 Store 等等。數據層至少要有一定的事件打通的功能。搞不好就牽一發動全身,改一個需求,不得不發布四五個項目。
前面提到過我們不希望沙盒是完全獨立的運行時環境,而是有分享和協同的可靠環境。這一章我們希望能說清我們希望沙盒提供怎樣的功能、如何使用。
到這里開始講到美好的景物了,docker。從解耦的角度看,服務端的微服務主要是通過 docker 技術實現虛擬化的底層支持,使服務開發者可以體會不到環境的區別、抹平運行時差異的。可以說對微服務來說,docker 是這些年能得到如此的發展的一個基石。
單純說微服務的概念本身,很久之前也有這個概念的,有面向服務編程的理論。但是發展很少,兌現仍然很困難,搞虛擬機很麻煩。而且還包括開發體驗的東西,我打包的鏡像——要想交付一致得包含整個操作系統嗎?這對開發體驗影響很壞。
在 docker 得到普遍應用之前,微服務在服務端的使用主要基于虛機。相比之下使用非常復雜、維護成本提高。虛擬機不說多麻煩了,大家都懂。它吃掉的資源,和容器化技術比完全不在一個量級。還有比如當你想要打個快照連磁盤都吃掉。并且多個服務之間的資源協調和有效分配,實現起來也極端困難。
諸多擴大的成本問題直到隨著 docker 的沙盒體系才得以解決。微服務才成為一個趨勢。可惜的是這樣的容器環境在前端瀏覽器內的運行時還不存在。
所以這里已經可以看出我們提出的前端沙盒、對它的期望,是讓它就像 Docker(而 iframe 在這個比喻里就相當于虛擬機)那樣。我們搞的這套機制是像 Docker 的前端運行時容器,像 Docker 一樣讓前端的拆分能輕松一點、分享容易一點、資源節約一點。當然這不是否定 iframe 的方案。
那么我們說的這種沙盒,這種輕量級、強調組件間協同溝通、非常節省資源的沙盒怎么做呢,下面我分 3 個方向分別介紹。(這塊還不會有具體的實現,主要從可能性上分析在瀏覽器里怎么造沙盒。)
參考單核、操作系統進程,模擬進程切換策略。 我們的沙盒實質上在讓一個瀏覽器去跑多個“獨立”的應用,那么這里對操作系統的模仿、最終趨同一定避不開。在這個角度上,和其他語言相比 JavaScript 占了一個獨特的執行特點的便宜:它自身是單線程的。我怎么做實質上也都是在一個線程內。相當于我們這個操作系統從一開始就限定了單核只有一個出力氣。
那么一個操作系統,怎么做多進程并行呢, 單進程可簡單通過根路由等等規則控制,每次只激活一個,大家做 context 切換即可;多進程并行就正好可以利用 JavaScript 的特性,我可以封裝每個獨立的事件循環。比如 setTimeout、各種事件回調的 handler,我們在實際 function 外面先切換 context,再執行你原本希望綁定的 function。這樣是線程安全的。總結下來就會是下面兩條:
用 context 切換來模擬線程安全, 具體的意思是在每個隔離的子應用“進程”即將開始激活時,先查找當前被激活的、其他的子應用,然后為這個將要退出的應用錄制“操作系統”的全現場狀態,保存為它的 context。最后為即將激活的新“進程”恢復、新建它自己的 context。
如上面所說,我把當前狀態記錄為 context,保證每個子應用都適用在自己的 context 內,不影響和改變別人的 context。這個操作全部由托管了子應用的父系統統一來切換。
本次重點講落地實踐,和一些踩坑經驗。歡迎大家持續關注,我們會輸出更多關于這部分的實踐經驗。
體現刪除 key 必須要遍歷兩次, 才能保證每個對象都遍歷到一遍。這塊要強調一個點,當你拿到新舊兩個對象比較,遍歷其中一個的 key、到另一個里面找,只這樣做是不夠的,因為有可能又刪掉的東西。刪掉導致 key 沒有了,自然也遍歷不到。想體現這個刪除,必須要遍歷兩次,新舊兩個對象都遍歷一遍,才能知道相比之下誰多了什么誰少了什么。尤其是大家做“空閑”到新開沙盒的比較的時候,特別容易忘了這個細節。
Context 切換的性能夠好嗎? 先說說這個快照的空間性能。如果你有 N 個沙盒需要有多少切換的組合呢,是不是 context 的全文、或者任意兩個沙盒之間的 context 差異都要完整儲存?實際上不用。我們只需要記錄差別、context 的變化,并且只記錄他對 “idle”狀態的差別。例如 A、B、C、D、E、F、G。我們不需要記錄 A→B A→C 這樣的切換,而是虛擬一個空閑狀態:O,都是 A→O,B→O,只保存他們和 O 之間的差別。需要記錄比較的變量數量從子應用個數的乘法變成了加法。寫一個循環就可以快速比較完變化。
綜上所述就是讓每個子應用的開始和結束、互相切換,都先回到一個虛擬的“初始狀態”,恢復現場,再進入被激活的沙盒狀態,每次切換僅記錄一個沙盒信息。避免了切換算法計算笛卡爾平方積導致比較、和保存沙盒切換信息過大的問題。
雖然這個章節的名字是字節跳動的實現,并且前面提到這次我們主要分享實現層面的技術細節,但我們不會僅僅探討和分享自己落地采用的方案,探討的內容也會包括研究過和對比過的。如果我們認為有好的、適合其他場景的技術方案,我們也都盡量分享出來。
先說 CSS 沙盒。 這塊 webComponent 已經做了很多發展了很多了。這里忍不住要說,web 標準里一度有一個非常吸引我、讓我感覺非常有意思的內容是 scoped css——就是加個 Attribute 就能結合 DOM 樹限制 CSS 作用范圍。后來這個標準被取消了。因為讓路給了 ShadowDOM 體系。
這個我不是很理解:因為 scoped CSS 是外面的規則能進來、里面的規則不出去,但 shadowDOM 是完全的割裂。這個巨大的區別使得它們的工程意義相差甚遠。后面我們會說到 css module,它的表現顯然和 scoped style 一樣,和 Shadow 不一樣。
CSS module 和 CSS in JS 都是把樣式寫成或編譯成腳本,同時把腳本生成的 DOM 的最外面一層加一個 nounce 的 attribute;然后再給所有受控的 CSS 規則都套上這個 "attribute"。缺點是相對麻煩了一點、并且要完全控制掌握所有 DOM 創建。在前端框架里,Angular 這樣做很自然。
后面還會提到這方面最流行的 NPM 包有個好玩的 feature 可能會造成不小心的 bug。
我們采用的是 DOM 沙盒保護 head 內標簽。 這樣的 style 和 link 本身都可以受到沙盒統一保護。在實際應用中我們的子應用開發者在業務組件里也有用 CSS module 的,我們也不用管——反正去掉標簽這個事情最安全。
DOM 沙盒就看管好某個 DOM 標簽,誰要改了,沙盒切換時改回來。對絕大多數情況綁定的 style 和 link 標簽都有效。但這個只限于單進程的情景。
如果如前面提到的多進程的情況(就是理解成同時有 N 個沙盒在一起運行、并行的系統)。那 CSS 肯定不能和 JavaScript 的單線程運行時一樣那么搞,所以一定要用 moduled CSS。也不難搞,很多開源庫可以用。即使出現大家引用同一個組件庫的不同版本、各自 hack 過、失手創造了什么“幺蛾子”也不用怕,因為他們都是編譯好、作用域有限了的。
用 NPM 上的 styled-component 包時要小心, 他們會根據環境變量判斷環境;然后對 prod 環境啟用一個叫“speedy”的模式,它將不用 innerText 寫樣式規則,而是用 addRules 那一整套 API。但是這套標準似乎沒明確界定這個標簽被從文檔 DOM 樹里移除時的行為和表現,也許因為顯而易見 rules 也應當一起移除。但我們插回來時,這種含糊就亂套了。瀏覽器實際的表現是移除再插回的標簽 rules 都沒了。這里顯然需要我們額外處理。
另一個重要的是全局變量干擾問題。 Polyfill 等運行環境相關的全局對象、環境變量等具體實現上有非常大的差別,又全部作用于全局。它們對子應用、模塊化的子組件來說,又屬于自身全局外部的環境。
這塊是微前端實施的一大重點。我個人覺得是這樣的。可以看出來大家都不是很信。“誰不知道不要寫全局變量啊?不會有這么不靠譜的人”。事實上真的試過才會發現會有好多。例如頭條號里面用到了某個剪裁圖片的插件庫。他是個非常完善、正派、和古典的包,同時支持 React 和 JQuery。它給全局寫了一個單例的實現。并且在開發調試過程中我們不同業務線的團隊就真的用了這個包的不同版本。
當然這個不重要,也沒有造成問題。一個比較嚴峻的例子是這個—— reGeneratorRuntime。它是編譯 async 語法用的,在某個 config 下的 Babel 會 delete 這個對象。到底是啥原理不清楚也不需要多講,但是非常肯定會沖突并造成問題。曾經我們的西瓜號團隊的 polyfill 規則和另一個業務線就發生了這類沖突。所以要比較 delete,恢復刪除,切換回去西瓜再刪掉。
Identifier 是另一個關注點。 你是否完全清楚 Identifier 是什么?Identifier 就是在某個 scope 下起作用的變量名啊什么的,包括 function,let,class,const。只有 var 出來的東西特殊一些、不會占用 Identifier,以上幾個會,占用后不可以重復用。
這些東西首先你遍歷不了,沒有枚舉器;其次他們不是某個對象的成員,而僅是編譯層面的名字。一旦產生了絕對刪不掉。
在全局作用域下 var a 的時候,實際上是生成了一個超范圍的 Identifier 并且額外在 global 上創建一個同名的 key,指向同一個地址。這是 var 語句額外的操作。這讓我們可以用遍歷 window 的方式來處置全局變量。
但如果你來個 const 就沒辦法了,沒有任何辦法。 class 也一樣。沒辦法枚舉也沒辦法刪除。最多就是再用 class 關鍵字聲明一下覆蓋掉。
總之這個事不要多想,new function 包起來幾乎必不可少。還可以傳入如 setTimeout 這種入參,用來控制異步實現“多進程”并行。
還有個 location 不要挪, 會刷新頁面。黑名單掉它。
還有個好玩的事:function 和 var 一樣會額外在 window 上增加個 key。這個 property 的 configurable 是 false——也就是不能刪除。但是可以賦值。
所以如果你如果光 var a,就可以 delete window.a;再寫 a 就是 undefined。寫個 function a,再寫個 delete a 就無效。 但如果你寫個 function a,再寫個 var a = 1。啥效果呢,你給 window 上綁了個刪不掉的數字,延續了 function a 的不可刪除屬性和 var a 的值。
更好玩的是 class,你 class B {} ,再 console log window.B,咋么樣,undefind。再寫 B = 1;然后再看 window.B 怎么樣?繼續 undefined 了, B = 1 沒有效果。
說明潛在的某個機制在 class 關鍵字執行的時候,給 global 綁上了個叫 B 的 property,但是是個無法枚舉和訪問的 property,property 有 writable true, enumerable: true, configurable: true 之外的隱藏屬性。
還有好多需要進程安全的對象, 比如 cookie,但這個其實不特別重要,簡單約定一個使用 path 就可以了——cookie 除了設置 domain 還可以設置 path。只不過大部分人都不設它(也就是設為根目錄“/”)。
localStorage 可以也保護一下。 取決于你的業務。因為這些都屬于 windows 的全局變量,所以實現一個包裝過的 class 集成并模擬 localStorage 原本的行為就可以。讓它所有方法都先給 key 加 prefix,再執行方法的 super。這個 prefix 可以簡單地寫死當前沙盒的 uuid 即可,因為 window.localStorage 作為全局變量本身就在沙盒保護之內。
下面是最后一章節,會講沙盒下有些特殊的東西,它們都需要額外處理。其中重點說一下埋點。多數微前端項目一個頁面里的埋點已經屬于不同項目了,這塊就得搞清楚具體什么子應用、用的什么統計代碼、需要處理哪些級別的緩存。
像前面說的,把 Storage 緩存全部用沙盒包裝過,對埋點體系來說還不算完。絕大多數埋點系統的事件發送都是異步、找網絡空閑的。并且這些源碼通常又在 SDK 里面、不在父工程直接控制的代碼里。所以可操作余地不多。實際上只能是把緩存數據、項目信息都好好保存好,再把收集數據的緩存和產生數據時的沙盒狀態對應起來。
沙盒可能會包一層或者多層運行時,所以 console 讀會比較累。開發的時候可以額外處理一下,為開發者提供便利。 而且現在的前端項目,線上希望 console 打印越少越好、內容越正規越好,并且調試又非常忌諱別人遺留的打印干擾。這些都是沙盒可以做的。我們甚至做了把內容直接對接到采集系統里的上傳 log。
具體來說,為 log 注入 callstack 是用 new Error 的方式。這樣可以通過 error.stack 拿到調用堆棧。這個值直接是個字符串、是換行分割的 Markdown,可以寫鏈接進去,也能對應到調試窗口的 source code。
同理的是當遇到真的 exception 也應當如此管控。從 catch 到異常、再次 throw 出去之前,可以給 error.stack 值全都 hack 掉。去掉不必要的 stack——比如你包的那層 new Function,刪掉那一行。提示的內容也可以改。
sourceMapping 是谷歌 closure 發明的一個、現在成為 ES6 標準的東西。原理是一個字符位置到字符位置的映射。那么在 new Function 下的沙盒里能不能用呢?當然可以。
我們先說說 new Function 的表現。在 chrome 里它在調試中是一個新的匿名環境:anonymous,字符行列位置就從函數字符串開頭第一行開始算起。如果你是把編譯并且生成了 sourceMap 后的 bundle 放到 new function 里執行,這個位置是完全對應的,不需要做任何額外的 hack。
這同時是因為 chrome 能正常識別 new Function 參數字符串末尾的 sourcemappingUrl= 的注釋。對應在 callstack 里一切都對。有好多時候我們發現業務方會不放心這個事,會覺得我直接下載的 .js 被包了,不放心調試的 call stack 和 sourceMap。事實上沒問題。這兩方面都沒問題。
另外我們之前其他場合分享也提到了,我們認為微前端的諸多必備條件,其中一個是要有一個服務發現資源和版本管理平臺,管理微前端的獨立發布、上下線和組合測試等等問題的。這樣順便也給了我們一個條件,可以給 sourceMap 管理起來。
上面就是本次分享的全部,分享了關于微前端沙盒字節跳動兩年來使用和實現的經驗,以及面對這些挑戰時的思考過程。非常幸運我們的項目有足夠多給力的伙伴們支持,最終獲得了比較大的成功,也非常明顯地提升了重量級的產品的質量。
微前端和很多前沿和剛剛發展的概念一樣,本身還在快速的演進和驗證的過程中,我們的具體實踐也一直在快速的變化,在不斷地發現弱點和糾正它們,也在努力發展更多的可能。在這個從種種不完美到更完美的奮斗過程中,能給讀者分享我們的成果是我們的一種榮幸。而且在分享后,如果能收到指教、討論和建議我們會更加感激,并且非常歡迎。也歡迎更多的有識之士加入我們,聯系請發往郵箱 aishiguang@bytedance.com 或關注 內推鏈接。
一、微前端概述
1. 什么是微前端?
為了解決龐大的一整塊后端服務帶來的變更與擴展方面的限制,出現了微服務架構。然而,越來越重的前端工程也面臨同樣的問題,自然地想到了將微服務思想應用(照搬)到前端,于是有了“微前端(micro-frontends)”的概念。即,一種由獨立交付的多個前端應用組成整體的架構風格。具體的,將前端應用分解成一些更小、更簡單的能夠獨立開發、測試、部署的小塊,而在用戶看來仍然是內聚的單個產品。
我們常見后臺項目通常長這樣:
1
如果我們的項目需要開發某個新的功能,而這個功能另一個項目已經開發好,我們想直接復用時。
說明:我們需要的只是別人項目的這個功能頁面的內容部分,不需要別人項目的頂部導航和菜單。
一個比較笨的辦法就是直接把別人項目這個頁面的代碼拷貝過來,但是萬一別人不是 vue 開發的,或者說vue 版本、UI 庫等不同,以及別人的頁面加載之前操作(路由攔截,鑒權等)我們都需要拷貝過來,更重要的問題是,別人代碼有更新,我們如何做到同步更新。甚至當別的項目采用其它技術棧時,如何集成?顯然復制代碼是行不通的。
以前端組件的概念作類比,我們可以把每個被拆分出的子應用看作是一個應用級組件,每個應用級組件專門實現某個特定的業務功能(如商品管理、訂單管理等)。這里實際上談到了微前端拆分的原則:即以業務功能為基本單元。經過拆分后,整個系統的結構也發生了變化:
如上圖所示,左側是傳統大型單頁應用的前端架構,所有模塊都在一個應用內,由應用本身負責路由管理,是應用分發路由的方式;而右側是基座模式下的系統架構,各個子應用互不相關,單獨運行在不同的服務上,由基座應用根據路由選擇加載哪個應用到頁面內,是路由分發應用的方式。這種方式使得各個模塊的耦合性大大降低,而微前端需要解決的主要問題就是如何拆分和組織這些子應用。
典型的基于vue-router的Vue應用與這種架構存在著很大的相似性:
2. 巨無霸項目的存在的問題
微前端的誕生也是為了解決以上問題:
使用微前端的好處:
二、常見微前端方案
目前主流的微前端方案包括以下幾個:
iframe:是傳統的微前端解決方案,基于iframe標簽實現,技術難度低,隔離性和兼容性很好,但是性能和使用體驗比較差,多用于集成第三方系統;
基座模式:主要基于路由分發,即由一個基座應用來監聽路由,并按照路由規則來加載不同的應用,以實現應用間解耦;
組合式集成:把組件單獨打包和發布,然后在構建或運行時組合。
EMP:基于Webpack5 Module Federation,一種去中心化的微前端實現方案,它不僅能很好地隔離應用,還可以輕松實現應用間的資源共享和通信。
Web Components:是官方提出的組件化方案,它通過對組件進行更高程度的封裝,來實現微前端,但是目前兼容性不夠好,尚未普及。
總的來說,iframe主要用于簡單并且性能要求不高的第三方系統;組合式集成目前主要用于前端組件化,而不是微前端;基座模式、EMP和Web Components是目前主流的微前端方案。
目前微前端最常用的有兩種解決方案:iframe 方案和 基座模式方案。
1. iframe方案
iframe 大家都很熟悉,使用簡單方便,提供天然的 js/css 隔離,也帶來了數據傳輸的不便,一些數據無法共享(主要是本地存儲、全局變量和公共插件),兩個項目不同源(跨域)情況下數據傳輸需要依賴postMessage 。
iframe 有很多坑,但是大多都有解決的辦法:
1. 頁面加載問題
iframe 和主頁面共享連接池,而瀏覽器對相同域的連接有限制,所以會影響頁面的并行加載,阻塞onload 事件。每次點擊都需要重新加載,雖然可以采用 display:none 來做緩存,但是頁面緩存過多會導致電腦卡頓。(無法解決)
2. 布局問題
iframe 必須給一個指定的高度,否則會塌陷。
解決辦法:子項目實時計算高度并通過postMessage 發送給主頁面,主頁面動態設置 iframe高度。有些情況會出現多個滾動條,用戶體驗不佳。
3. 彈窗及遮罩層的問題
彈窗只能在 iframe 范圍內垂直水平居中,沒法在整個頁面垂直水平居中。
解決辦法1:通過與框架頁面消息同步解決,將彈窗消息發送給主頁面,主頁面來彈窗,對原項目改動大且影響原項目的使用。
解決辦法2:修改彈窗的樣式:隱藏遮罩層,修改彈窗的位置。
4. iframe 內的 div 無法全屏
彈窗的全屏,指的是在瀏覽器可視區全屏。這個全屏指的是占滿用戶的屏幕。
全屏方案,原生方法使用的是Element.requestFullscreen(),插件:vue-fullscreen。當頁面在 iframe 里面時,全屏會報錯,且dom 結構錯亂。
解決方案:iframe 標簽設置 allow="fullscreen" 屬性即可
5. 瀏覽器前進/后退問題
iframe 和主頁面共用一個瀏覽歷史,iframe 會影響頁面的前進后退。大部分時候正常,iframe 多次重定向則會導致瀏覽器的前進后退功能無法正常使用。并且 iframe 頁面刷新會重置(比如說從列表頁跳轉到詳情頁,然后刷新,會返回到列表頁),因為瀏覽器的地址欄沒有變化,iframe 的 src 也沒有變化。
6. iframe 加載失敗的情況不好處理
非同源的 iframe 在火狐及 chorme 都不支持onerror 事件。
解決辦法1:onload 事件里面判斷頁面的標題,是否 404 或者 500
解決辦法2:使用 try catch 解決此問題,嘗試獲取 contentDocument 時將拋出異常。
2. 基座模式方案
基座模式方案以single-spa和qiankun為代表,這里我選擇qiankun。
qiankun 是螞蟻金服開源的一款框架,它是基于single-spa 的。他在 single-spa 的基礎上,實現了開箱即用,除一些必要的修改外,子項目只需要做很少的改動,就能很容易的接入。
qiankun框架官網:https://qiankun.umijs.org/zh/。
微前端中子項目的入口文件常見的有兩種方式:JS entry 和 HTML entry。純 single-spa 采用的是 JS entry,而 qiankun 既支持 JS entry,又支持 HTML entry。
JS entry 的要求比較苛刻:
(1)將 css 打包到 js 里面
(2)去掉 chunk-vendors.js,
(3)去掉文件名的 hash 值
(4)將 single-spa 模式的入口文件( app.js )放置到 index.html 目錄,其他文件不變,原因是要截取app.js 的路徑作為 publicPath。
建議使用 HTML entry ,使用起來和 iframe 一樣簡單,但是用戶體驗比 iframe 強很多。qiankun 請求到子項目的 index.html 之后,會先用正則匹配到其中的 js/css 相關標簽,然后替換掉,它需要自己加載 js并運行,然后去掉 html/head/body 等標簽,剩下的內容原樣插入到子項目的容器中 。
二、微前端方案實踐
以“大數據分析”項目為例,將客戶特有的需求,如“電子路單”、“數據填報”單獨提取為可獨立運行的子項目。
大數據分析項目改造為主應用基座,代碼倉庫地址:http://192.168.1.102/zouqiongjun/big-data-web.git。
客戶自定義需求單獨作為子應用項目,代碼倉庫地址:http://192.168.1.102/zouqiongjun/zibo-custom-web.git。
1. 各應用工程代碼結構
sass-base-web:主倉庫,主要存放一些批量操作的腳本,用于聚合管理倉庫和一鍵編譯、一鍵部署。
倉庫代碼結構如下圖所示:
big-data-web:大數據分析主應用
zibo-custom-web:客戶自定義需求,微應用倉庫
子應用可以獨立運行,但是當前子應用是直接嵌套在主應用的main內容區域,所以暫時并沒有單獨提供左側菜單導航,后續如有需要可以擴展和補充此功能。
2. 主應用big-data-web改造
將普通的項目改造成 qiankun 主應用基座,需要進行三步操作:
(1) 創建微應用容器 - 用于承載微應用,渲染顯示微應用;
(2) 注冊微應用 - 設置微應用激活條件,微應用地址等等;
(3) 啟動 qiankun;
注意:由于big-data-web主應用的路由采用的是hash模式,所以子應用的路由也應該采用hash模式。
1.1 安裝 qiankun
$ yarn add qiankun # 或者 npm i qiankun -S
1.2. 在主應用中注冊微應用
為了使用keepAlive緩存,這里我們采用手動加載微應用的方式。
當微應用信息注冊完之后,一旦瀏覽器的 url 發生變化,便會自動觸發 qiankun 的匹配邏輯,所有activeRule 規則匹配上的微應用就會被插入到指定的container 中,同時依次調用微應用暴露出的生命周期鉤子。
在views目錄下,新建AppVueHash.vue,作為子應用的容器,代碼如下:
<template>
<div class="zibo-custom-web">
<div id="zibo-custom-web" class="app-view-box"></div>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss" scoped>
.zibo-custom-web{
position: relative;
}
</style>
這個id屬性要唯一,最終子應用的內容將會掛載在這里。
ContainerOther.vue代碼改造:
<!-- 主體視圖層 -->
<div class="avue-view-contain" v-show="!isSearch">
<keep-alive>
<router-view
class="avue-view keep-alive"
v-if="$route.meta.keepAlive && isActiveRoute"
v-show="!showAppVueHash"
/>
</keep-alive>
<router-view
class="avue-view"
v-if="!$route.meta.keepAlive && isActiveRoute"
v-show="!showAppVueHash"
/>
<AppVueHash v-show="showAppVueHash" />
</div>
js代碼:
import router from "@/router/router";
import store from "@/store";
import AppVueHash from "@/views/AppVueHash.vue";
import { loadMicroApp } from "qiankun";
//子項目路由前綴
const isChildRoute = path => website.childRoute.some(item => path.startsWith(item));
const apps = [
{
name: "/zibo-custom-web",
entry: window.configs.VUE_APP_ZIBO_CUSTOM_URL,
container: "#zibo-custom-web",
props: { data: { store, router } },
sandbox: {
strictStyleIsolation: true // 開啟樣式隔離
}
}
];
//控制微應用手動加載
ctrlMicroApp(path){
if (isChildRoute(path)) {
this.showAppVueHash = true;
this.$nextTick(() => {
//手動加載
if(!this.mounted){
this.loadApps = apps.map(item => loadMicroApp(item))
this.mounted=true;
}
});
} else {
this.showAppVueHash = false;
}
這里的container屬性值,必須和AppVueHash.vue組件中的id值保持一致。
根據url地址判斷是否是子應用,如果是子應用,則手動加載,否則隱藏子應用容器,只加載主應用的router-view。
在ContainerOther第一次加載或路由變化時手動加載微應用:
mounted() {
this.init();
setTheme(this.themeName);
this.ctrlMicroApp(this.$route.path)
},
watch: {
$route(val) {
this.ctrlMicroApp(val.path);
},
tagList(newVal,oldVal){
let starts='';
const childRoute = website.childRoute;
childRoute.forEach((n,i)=>{
if(i<childRoute.length-1){
starts+=`^${n}|`;
}else{
starts+=`^${n}`;
}
})
const patt = new RegExp(`${starts}`);
//之前存在子應用頁簽
const before = oldVal.some(item=>{
return patt.test(item.value);
});
//現在存在子應用頁簽
const now = newVal.some(item=>{
return patt.test(item.value);
});
if(before && !now){
this.mounted=false;
this.loadApps.forEach(app=>{
app.unmount();
})
}
}
監聽tab頁簽變化,關閉頁簽時,需要卸載子應用。
3. qiankun 子項目zibo-custom-web
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
<div id="appVueHash"></div>
// -----------子應用微前端start-------------------
let router = null;
let instance = null;
function render({ data = {} , container } = {}) {
router = new VueRouter({
routes,
});
instance = new Vue({
router,
store,
data(){
return {
parentRouter: data.router,
parentVuex: data.store,
}
},
render: h => h(App),
}).$mount(container ? container.querySelector('#appVueHash') : '#appVueHash');
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
//測試全局變量污染
console.log('window.a',window.a)
export async function bootstrap() {
console.log('vue app bootstraped');
}
export async function mount(props) {
console.log('props from main framework', props.data);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = "";
instance = null;
router = null;
}
// -----------子應用微前端end-------------------
主要改動是:引入修改 publicPath 的文件和export 三個生命周期。
注意:
const { name } = require("./package");
module.exports = {
outputDir: "../sass-base-web/cicd-config/test/zibo-custom-web",
devServer: {
port: 9010,
// 關閉主機檢查,使微應用可以被 fetch
disableHostCheck: true,
// 配置跨域請求頭,解決開發環境的跨域問題
headers: {
"Access-Control-Allow-Origin": "*",
},
proxy: {
"/api": {
//本地服務接口地址
target: "http://192.168.10.112:10067", //cas
ws: true,
pathRewrite: {
"^/api": "/",
},
},
},
},
// 以下配置可以修復一些字體文件加載路徑問題
chainWebpack: (config) => {
//忽略的打包文件
config.externals({
vue: "Vue",
"vue-router": "VueRouter",
vuex: "Vuex",
axios: "axios",
"element-ui": "ELEMENT",
});
config
.plugin('html')
.tap(args => {
args[0].name = name;
return args
});
config.module
.rule("fonts")
.test(/.(ttf|otf|eot|woff|woff2)$/)
.use("url-loader")
.loader("url-loader")
.tap((options) => ({ name: "/fonts/[name].[hash:8].[ext]" }))
.end();
},
// 自定義webpack配置
configureWebpack: {
output: {
library: `${name}-[name]`, // 微應用的包名,這里與主應用中注冊的微應用名稱一致
libraryTarget: "umd", // 把子應用打包成 umd 庫格式
jsonpFunction: `webpackJsonp_${name}`, //webpack打包之后保存在window中的key,各個子應用不一致
},
},
};
這里主要就兩個配置,一個是允許跨域,另一個是打包成 umd 格式。為什么要打包成 umd 格式呢?是為了讓 qiankun 拿到其 export 的生命周期函數。
注意: 這個 name 默認從 package.json 獲取,可以自定義,只要和父項目注冊時的 name 保持一致即可。
vue.config.js中
outputDir: "../sass-base-web/cicd-config/test/zibo-custom-web",
這里我子應用項目編譯后會將打包文件打包到sass-base-web項目中的zibo-custom-web下。
需要給子項目所有的路由都添加一個前綴,子項目的路由跳轉如果之前使用的是 path 也需要修改,用name 跳轉則不用。
avue-router.js:
const oRouter = {
path: "/zibo-custom-web",
name: "RouterView",
component(resolve) {
require(["@/components/RouterView.vue"], resolve);
},
children: [
{
path: path,
component(resolve) {
require([`../${component}.vue`], resolve);
},
name: name,
meta: meta,
},
],
};
aRouter.push(oRouter);
4. 狀態管理,主應用和微應用之間的通信
qiankun 通過 initGlobalState: 定義全局狀態,并返回通信方法,建議在主應用使用,微應用通過props 獲取通信方法;
onGlobalStateChange: 在當前應用監聽全局狀態,有變更觸發 callback;
setGlobalState: 按一級屬性設置全局狀態,微應用中只能修改已存在的一級屬性; 換句話說只能修改主用于預先定義的屬性,后面添加的屬性無效。
官方例子:發布-訂閱的設計模式:
主應用:
import { initGlobalState, MicroAppStateActions } from 'qiankun';
// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);
actions.onGlobalStateChange((state, prev) => {
// state: 變更后的狀態; prev 變更前的狀態
console.log(state, prev);
});
actions.setGlobalState(state);
actions.offGlobalStateChange();
子應用:
// 從生命周期 mount 中獲取通信方法,使用方式和 master 一致
export function mount(props) {
props.onGlobalStateChange((state, prev) => {
// state: 變更后的狀態; prev 變更前的狀態
console.log(state, prev);
});
props.setGlobalState(state);
}
如果主應用和子應用都是vue技術棧,可以在子應用中把數據傳遞給子應用,例如store。
props: { data: { store, router } },
5. 各應用之間的獨立倉庫以及聚合管理
實際開發中項目存儲在公司倉庫中,以 gitLab 為例, 當子應用一多,全部放在一個倉庫下面, 這時候就顯得很臃腫了,也很龐大,大大的增加了維護成本,和開發效率;
我們可以通過 sh 腳本, 初始只需要克隆主倉庫代碼, 然后通過 sh 腳本去一鍵拉取所有子應用代碼。
這里我將單獨創建一個用來編譯和打包的主倉庫項目sass-base-web,倉庫地址:http://192.168.1.102/zouqiongjun/sass-base-web.git。
項目根目錄下,新建 script/clone-all.sh 文件,內容如下:
# 子服務 gitLab 地址
SUB_SERVICE_GIT=('http://192.168.1.102/zouqiongjun/big-data-web.git' 'http://192.168.1.102/zouqiongjun/zibo-custom-web.git')
SUB_SERVICE_NAME=('big-data-web' 'zibo-custom-web')
# 子服務
if [ ! -d "sub-service" ]; then
echo '創建sub-service目錄...'
mkdir sub-service
fi
echo '進入sub-service目錄...'
cd sub-service
# 遍歷克隆微服務
for i in ${!SUB_SERVICE_NAME[@]}
do
if [ ! -d ${SUB_SERVICE_NAME[$i]} ]; then
echo '克隆微服務項目'${SUB_SERVICE_NAME[$i]}
git clone ${SUB_SERVICE_GIT[$i]}
fi
done
echo '腳本結束...'
# 克隆完成
當我們啟動主項目的時候,如果想要使用所有子應用的功能,子應用,需要一個一個啟動,這樣無論是開發還是編譯都十分不便。
考慮到國內使用npm裝包可能會由于網絡的原因安裝失敗,可以讓npm使用國內淘寶鏡像。
執行命令:npm config set registry https://registry.npm.taobao.org。
在這個主倉庫項目里面,我們只需要安裝一個npm-run-all插件。
運行:yarn add npm-run-all -D或者npm i -D。
該項目下只有一個package.json文件,用于配置編譯和打包命令,代碼如下:
{
"name": "sass-big-data-web",
"version": "1.0.0",
"description": "`qiankun`來實現`vue`技術棧的前端微服務",
"main": "index.js",
"scripts": {
"clone:all": "bash ./scripts/clone-all.sh",
"install:zibo": "cd ./sub-service/zibo-custom-web && npm install",
"install:main": "cd ./sub-service/big-data-web && npm install",
"install-all": "npm-run-all install:*",
"start:zibo": "cd ./sub-service/zibo-custom-web && npm run serve ",",
"start:main": "cd ./sub-service/big-data-web && npm run serve",
"start-all": "npm-run-all --parallel start:*",
"serve-all": "npm-run-all --parallel start:*",
"build:zibo": "cd ./sub-service/zibo-custom-web && npm run build",
"build:main": "cd ./sub-service/big-data-web && npm run build",
"build-all": "npm-run-all --parallel build:*"
},
"repository": {
"type": "git",
"url": "http://192.168.1.102/zouqiongjun/sass-big-data-web.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
要執行yarn clone:all,需要用到bash,跳轉到sass-base-web目錄,右鍵鼠標打開Git bash窗口,如下圖所示:
這樣當我們把各個代碼倉庫的代碼都拉取到sub-service這個目錄下面來,如下圖所示:
代碼拉取完成后, 緊接著就是下載各個項目的依賴及運行。
運行npm run serve-all則可以自動執行package.json中配置的命令,這個命令最終會執行以下三個執行命令:
"start:zibo": "cd ../zibo-custom-web && npm run serve ",
"start:main": "cd ../big-data-web && npm run serve",
這樣就不需要我們自己一個一個單獨的去運行各個項目了。
總體運行步驟: 第一步 clone 主應用, 然后依次執行 yarn clone:all --> yarn install-all --> yarn start-all 即可運行整個項目。
build-all:可以編譯整個項目。
sub-service目錄,將其添加到.gitignore當中,因為在sass-base-web項目當中,我們只需要配置和編譯及打包用,并不需要真正的將所有子應用的代碼都提交到sass-base-web項目中,各子應用都有自己私有的倉庫。
6. 子項目開發的一些注意事項
(1)所有的資源(圖片/音視頻等)都應該放到src 目錄,不要放在 public 或者static。資源放 src 目錄,會經過 webpack 處理,能統一注入 publicPath。否則在主項目中會404。
(2)避免 css 污染。組件內樣式的 css-scoped是必須的。
(3)給 body 、 document 等綁定的事件,請在unmount 周期清除
(4)謹慎使用 position:fixed
在父項目中,這個定位未必準確,應盡量避免使用,確有相對于瀏覽器窗口定位需求,可以用position: sticky,但是會有兼容性問題(IE不支持)。
常見問題見官網:https://qiankun.umijs.org/zh/faq
7. 部署
1. 常規部署
主應用和微應用都是獨立開發和部署,即它們都屬于不同的倉庫和服務。
場景:主應用和微應用部署到同一個服務器(同一個IP和端口)
如果服務器數量有限,或不能跨域等原因需要把主應用和微應用部署到一起。通常的做法是主應用部署在一級目錄,微應用部署在二/三級目錄。
若微應用想部署在非根目錄,在微應用打包之前需要做兩件事:
部署之后注意三點:
通過配置 nginx 端口到目錄的轉發。須要對外開放子利用對應的端口,將編譯好的利用文件放到對應的配置目錄。
跳轉到sass-base-web目錄,執行npm run build-all,會自動執行所有build:開頭的命令:
"build:zibo": "cd ../zibo-custom-web && npm run build",
"build:control": "cd ../control-center && npm run build",
"build:main": "cd ../big-data-web && npm run build",
"build-all": "npm-run-all --parallel build:*"
zibo-custom-web目錄結構如下圖所示:
這里是子應用和主應用部署在同一臺服務器上,且IP和端口相同,nginx不需要額外設置。
如果子應用和主應用部署在同一臺服務器上, 但是端口不同,需要修改vue.config.js中的outputDir,這個是打包編譯后代碼存放的路徑,這個不做配置,默認會將代碼編譯打包到當前根目錄下,并生成一個dist目錄用于存放編譯后的代碼,如下圖所示:
修改nginx.conf配置:
#gzip on;
upstream gateway { server 192.168.31.136:32067;}
# 主應用
server {
listen 32043;
server_name web;
root /dist;
# 關閉端口重定向
# port_in_redirect off;
#charset koi8-r;
access_log /var/log/nginx/nginx.log;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ^~/api/ {
proxy_read_timeout 600s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://gateway;
}
}
# 子應用
server {
listen 9010;
server_name cus_web;
# 子應用編譯后的代碼路徑
root /zibo-custom-web;
# 允許跨域
add_header Access-Control-Allow-Origin *;
# 關閉端口重定向
# port_in_redirect off;
# charset koi8-r;
access_log /var/log/nginx/nginx.log;
location ^~/zibo-custom-web/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ^~/api/ {
proxy_read_timeout 600s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://gateway;
}
}
2. docker nginx 配置
此處 nginx 主要作用是用于端口目錄轉發,并配置主應用訪問子應用的跨域問題。
使用 docker 配置部署 nginx:
# docker-compose.yml
version: '3.1'
services:
nginx:
restart: always
image: nginx
container_name: nginx
ports:
- 8888:80
- 8889:8889
- 7100:7100
- 7101:7101
volumes:
- /app/volumes/nginx/nginx.conf:/etc/nginx/nginx.conf
- /app/volumes/nginx/html:/usr/share/nginx/html
- /app/micro/portal:/app/micro/portal
- /app/micro/app1:/app/micro/app1
- /app/micro/app2:/app/micro/app2
將編譯后的主應用以及子應用放到對應的數據卷掛載目錄即可,如主應用 /app/micro/portal。 同理,也需要將配置好的 nginx.conf 文件放到指定的數據卷掛載目錄,使用 docker-compose up -d 啟動即可。
nginx 端口目錄轉發配置:
# nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server {
listen 8889;
server_name 192.168.2.192;
location / {
root /app/micro/portal;
index index.html;
try_files $uri $uri/ /index.html;
}
}
server {
listen 7100;
server_name 192.168.2.192;
# 配置跨域訪問,此處是通配符,嚴格生產環境的話可以指定為主應用 192.168.2.192:8889
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
location / {
root /app/micro/app1;
index index.html;
try_files $uri $uri/ /index.html;
}
}
server {
listen 7101;
server_name 192.168.2.192;
# 配置跨域訪問,此處是通配符,嚴格生產環境的話可以指定為主應用 192.168.2.192:8889
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
location / {
root /app/micro/app2;
index index.html;
try_files $uri $uri/ /index.html;
}
}}
部署到生產,需要修改big-data-web/public/util/config.js中的VUE_APP_ZIBO_CUSTOM_URL配置項:
(function() {
window.configs = {
VUE_APP_CASLOGINURL: "http://192.168.1.99:32080/cas", //cas登錄地址
VUE_APP_REDIRECTURL: "http://192.168.51.61:8888/big-data/", //前端部署地址
VUE_APP_SOCKET: "ws://192.168.31.136:32061", //websocket地址'
VUE_APP_AMAPURLPREFIX: "https://webapi.amap.com", //高德地圖地址
VUE_APP_ZIBO_CUSTOM_URL:"http://localhost:9010",//自定義微應用地址
//================================================
VUE_APP_AMAPKEY: "xxxxxx" //高德key
};
})();
這里config.js是為了配置文件外置,不需要編譯。
8. 總結
盡管qiankun框架支持各個子應用使用不同的技術框架,但是都需要子應用做相應的改造,而且在其它技術棧中總是會時不時的出現各種難以預料的錯誤,一旦出現問題,都需要我們去改造代碼。所以如果都是vue技術棧,建議使用qiankun做微前端。
如果是第三方公司的項目接入進來,由于他們的代碼不受我們控制,需要酌情考慮是否用iframe。
參考文獻:qiankun 微前端方案實踐及總結
生志愿是考生本人升學意愿的真實表達,是投檔及錄取的重要依據。志愿一定要由考生本人填報,考生須對其所填報的志愿內容和填報操作負全責。
為了幫助考生填報志愿,我們編寫“2021年河北省普通高考志愿填報須知”,供考生參考。
一、志愿填報的流程及注意事項
(一)志愿填報時間是如何安排的?
志愿填報期間,每日0時至6時為系統維護時間,不能填報志愿。
(二)志愿填報的流程是什么?
考生志愿填報采取遠程網上填報方式進行。
考生須使用IE11、Chrome、Edge瀏覽器,其他類型及版本不予支持,由于瀏覽器類型及版本使用不當造成的一切后果由考生自負。在規定時間內,考生可通過河北省教育考試院官方網站(http://www.hebeea.edu.cn),點擊相應鏈接進入普通高校招生志愿填報系統,也可以訪問https://gk.hebeea.edu.cn,通過河北省普通高校招生考試信息管理與服務平臺進入系統,進行志愿填報。系統暫不支持使用手機、平板電腦等移動終端填報志愿。
具體填報流程如下圖所示:
1.考生須使用用戶名和密碼登錄系統,用戶名為考生本人的考生號或身份證號,密碼為高考報名時考生本人所設置的密碼。
2.首次登錄時,考生須認真閱讀《河北省2021年高考志愿填報承諾書》,并確認承諾。
3.考生在志愿填報規定時間內,點擊“填報”按鈕,進入“2021年河北省普通高校招生志愿填報表”頁面。
4.點擊所要填報的批次(段),進入該批次(段)志愿填報頁面。依次選擇計劃性質、科類(填報高校專項計劃、高水平運動隊、高水平藝術團的考生還須選擇獲得的資格類型),然后輸入院校代號、專業代號(順序志愿還需選擇是否“服從專業調劑”),填報完成后須點擊“保存”按鈕進行志愿保存。保存時需要輸入系統登錄密碼。保存成功后系統會給出“XXXX批次志愿保存成功!”提示。點擊“返回填報表”按鈕可返回到“2021年河北省普通高校招生志愿填報表”頁面,所報志愿會在“所填志愿”欄中顯示出來。
在填報平行志愿(不含對口)時,需要提醒的是:
①志愿填報系統提供了多種調整志愿順序的方法:清空、插入、上移、下移、刪除、調整、拖動。順序調整完成后,須點擊“保存”按鈕,否則調整志愿順序無效。
②志愿填報頁面左側的“志愿導航”欄可以拖動,方便考生快速定位及志愿保存。
5.考生在填報過程中如需修改志愿信息,可在“批次入口選擇”欄中點擊相應批次(段)名稱,進入志愿修改頁面,在規定時間內進行志愿修改。在志愿填報截止時間前,考生可多次登錄系統進行志愿修改和保存,最終以其網上最后一次修改并保存成功的志愿為準。
6.考生志愿核查無誤后,點擊“安全退出”按鈕安全退出系統。
7.每個階段志愿填報結束后,系統將關閉3小時(以實際操作時間為準)進行系統維護。系統維護完成并重新開放后,考生可登錄志愿填報系統,查看自己所填報的志愿信息。
(三)志愿填報注意事項是什么?
1.志愿填報系統是“一個批次(段)一保存”,而不是全部批次填報后統一保存,考生每批次(段)填報志愿后,必須點擊“保存”按鈕進行志愿保存,否則所填志愿無效。
2.考生應在規定時間內填報志愿。未在規定時間內完成網上填報志愿的考生視為自動放棄志愿填報。
3.考生應增強自身信息安全意識。志愿填報要由考生本人操作,不要讓他人代為操作。請考生不要將密碼設置得過于簡單,并妥善保管密碼,不要將密碼告訴他人,以防被他人篡改志愿,影響錄取。因考生本人泄漏密碼或由他人代為填報而造成的一切后果由考生承擔。
4.考生要按照志愿順序填報平行志愿。在進行志愿保存時,所填志愿序號必須連續,中間不能存在為空情況。例如填報本科批志愿時,始終未填序號1(第一行)的院校及專業,而跳過去直接填了序號2(第二行)的院校及專業,志愿填報系統不予支持。
5.多名考生使用同一臺計算機填報志愿時,每位考生填報志愿后一定要點擊“安全退出”按鈕,并關閉所有顯示本人信息的頁面,否則,后一位考生所填報的志愿信息可能會覆蓋前面考生的志愿信息。同時還要注意,不要同時打開兩個或兩個以上填報頁面,以免發生錯誤。
6.考生在上網填報之前應先填好紙質《2021年河北省普通高校招生考生志愿填報草表》(屆時可從省教育考試院官網下載打印),盡量不要在網上一邊思考一邊填報。這樣既可以提高網上填報志愿的效率和準確度,同時又避免造成網絡堵塞。考生登錄系統后20分鐘內無任何操作,再次進行操作時,系統會進行“考生未登錄”提示,或被強制退出系統,且未進行保存過的志愿不會保存。考生須重新登錄系統進行填報。
7.考生應盡早上網填報志愿,避免因網絡、供電等原因而影響志愿填報。
8.對填報志愿中系統給出填報有誤提示,考生應從批次(段)、計劃性質、報考科類、院校代號、專業代號、選科要求、性別要求,器種、唱法或舞種要求和本人情況等幾方面逐一查找出錯原因。如果志愿信息存在錯誤無法進行確認,請在修改信息后再進行確認,否則可能導致志愿信息無效或不能體現本人最終意愿。
(四)填報志愿時考生忘記登錄密碼怎么辦?
遺忘密碼的考生可通過以下途徑進行密碼重置,重置后的密碼為考生身份證號后8位。
1.通過手機微信重置密碼。考生使用手機添加“河北省教育考試院”微信公眾號,進入密碼找回頁面,通過人臉識別方式自助進行密碼重置。
2.考生使用能夠訪問互聯網并裝有攝像頭的計算機,登錄河北省教育考試院普通高校招生考試信息管理與服務平臺,進入志愿填報或考生信息查詢系統,然后進入“密碼重置”頁面,通過人臉識別方式自助進行密碼重置。
3.到就近報名點重置密碼。考生攜帶本人身份證到任一報名點,通過讀取身份證、人臉識別完成密碼重置。
提醒考生注意:密碼重置完成后,要及時登錄系統修改初始密碼。考生務必牢記并妥善保管,避免因密碼泄漏造成不良后果。
二、志愿填報前考生需要做哪些準備?
在填報志愿前,要充分了解自己的成績及所處的位次、招生政策、高校招生章程、招生計劃等相關信息,以節省填報時間、提升填報效率,提高填報的準確度。
一是學習相關政策。考生要對當年的高考政策有一個整體的把握,清楚招生錄取的相關辦法和規定,熟悉各類別的批次設置、志愿設置、投檔規則、錄取辦法以及志愿填報時間和流程。今年是我省高考綜合改革落地之年,考生可登錄省教育廳、省教育考試院網站,查詢《河北省普通高考綜合改革政策解讀“50問”》(網址:https://mp.weixin.qq.com/s/TqyMxWseXo09TFWhXUHIBw)和《河北省2021年普通高校招生考試和錄取工作實施方案解讀》(網址:http://www.hebeea.edu.cn/html/ptgk/tzgg/2020/1229-180049-826.html),了解相關政策。
二是查看高校招生計劃。招生計劃內容包括計劃性質、批次、科類、院校、辦學類型、專業、選考科目要求、學制、學費、專業備注等信息。考生可通過河北省教育考試院匯編的《2021年河北省普通高等學校招生計劃》(物理科目組合/歷史科目組合·對口)進行查詢。
三是閱讀高校招生章程。考生可登錄高校官方網站或教育部陽光高考信息公開平臺,查閱相關高校2021年招生章程。招生章程主要內容包括:高校全稱、校址(涉及分校、校區等須注明),層次(本科、專科),辦學類型(如普通或成人高校、公辦或民辦高校或獨立學院、高等專科學校或高等職業技術學校等),招生計劃分配的原則和辦法,預留計劃數及使用原則,專業教學培養使用的外語語種,身體健康狀況要求,進檔考生錄取規則(如對考生加分成績的使用、投檔成績相同考生的處理、進檔考生的專業安排辦法等),學費標準,家庭經濟困難學生資助政策及有關程序,頒發學歷證書的學校名稱、證書種類及其他信息,聯系電話、網址,以及其他須知等。
四是了解《普通高等學校招生體檢工作指導意見》。其中規定了對患有某些疾病或有生理缺陷者不宜報考的專業。考生應根據體檢結果,對照體檢工作指導意見和有關高校的招生章程,避開自己不宜報考的專業。
五是了解自己所在的位次。高考成績公布后,省教育考試院將在官網、官微公布當年“河北省普通高校招生各類考生成績統計表”。屆時,考生可進行查看,供填報志愿參考。
六是參考其他資料。包括《全國普通高校在河北招生錄取分數分布統計2018—2020》《2021年河北省普通高校招生報考指南》《2021年河北省普通高等學校招生藝術類報考指南》。其中,《全國普通高校在河北招生錄取分數分布統計2018—2020》主要內容為近三年全國普通高校在河北省招生錄取情況,包括分學校、分專業的錄取數、最高分、最低分、平均分、差值及各專業錄取分數分布統計;《2021年河北省普通高校招生報考指南》和《2021年河北省普通高等學校招生藝術類報考指南》介紹了我省高考錄取的相關政策規定、操作程序和注意事項,并收集整理了近三年高校錄取的相關數據和資料,這些資料為考生填報志愿作參考。
七是分析研究相關信息。收集分析相關高校的往年錄取數據等多種信息,結合自己的學習興趣、專業取向和未來發展規劃,綜合考慮,理性選擇。
八是利用好《2021年河北省普通高校招生考生志愿填報草表》,按照批次、計劃性質、科類或資格類型等提前進行志愿預選。
三、招生計劃是如何編制的?
各高校在我省的招生計劃,經其教育主管部門審核、匯總后上報教育部,再由教育部分送給我省。我院按照我省的格式對計劃內容進行了編排、匯總,由高校核對確認后統一向社會公布。
2021年我省普通高校招生,普通類、體育類按物理科目組合、歷史科目組合分別編制招生計劃。藝術類不區分物理科目組合、歷史科目組合,統一編制招生計劃。對于只招首選科目為歷史的考生或只招首選科目為物理的考生的藝術類專業,我省根據院校的要求在招生計劃中予以公布。
首選科目為物理的普通類、體育類考生,查詢《2021年河北省普通高等學校招生計劃》(物理科目組合),首選科目為歷史的普通類、體育類考生,查詢《2021年河北省普通高等學校招生計劃》(歷史科目組合·對口)。為便于考生查閱,藝術類專業招生計劃在《2021年河北省普通高等學校招生計劃》(物理科目組合)和《2021年河北省普通高等學校招生計劃》(歷史科目組合·對口)中均有編列,但它們是同一計劃,并非物理科目組合和歷史科目組合各自單獨的招生計劃數(個別專業單獨注明只招首選科目為歷史或只招首選科目為物理的考生的除外)。按照招生院校要求,對首選科目有要求的藝術類專業,我省招生計劃中相應注明“[只招首選科目為[歷史]考生]”或“[只招首選科目為[物理]考生]”,請考生注意查看,不要填報不符合選考科目要求的專業。
對口類招生計劃編制原則和方式與往年相同,對口考生查詢計劃可參考《2021年河北省普通高等學校招生計劃》(歷史科目組合·對口)。
提醒考生注意:
一是考生在志愿填報系統中所填的院校代號、專業代號等內容,均以《2021年河北省普通高等學校招生計劃》(物理科目組合/歷史科目組合·對口)所公布的為準。未經省教育考試院公布的招生高校和計劃,一律不安排在河北省招生。考生要特別注意《招生計劃》中擬報考院校(專業)的計劃性質、科類及其所在的錄取批次(段),并在志愿填報時正確選擇。
二是《招生計劃》中部分院校(專業)后注明了院校(專業)的辦學地點、學費標準和外語語種、單科成績等限制條件,也有部分院校沒有注明,但是不代表學校沒有要求,所以請考生報考前務必查詢院校招生章程或向招生院校咨詢。在招生過程中,一些院校的計劃內容可能有變動,考生在填報志愿前,應了解各招生院校(專業)的具體情況和報考要求。
三是為使考生更加清楚地了解高校,今年招生計劃的院校名稱后面添加了辦學類型。
四、在查閱招生計劃時要注意哪些問題?
考生查閱招生計劃時,需注意選考科目、辦學性質、專業備注內對考生性別、外語語種、身體條件和面試等方面的要求。招生計劃展現給考生的是同一院校下多個專業,考生填報順序志愿時應先填寫院校代號,再填寫不超過6個專業代號,并根據自身實際決定是否勾選專業服從調劑選項,從而構成1個志愿;考生填報“專業(類)+學校”平行志愿時應先填寫院校代號,再填寫1個專業代號,從而構成1個志愿。
五、改革后的普通高校招生錄取依據有什么變化?
從2021年起,我省普通高校招生依據統一高考和高中學業水平選擇性考試成績,參考綜合素質評價進行錄取。
六、綜合素質評價在錄取時如何使用?
普通高中學生綜合素質評價作為高校招生錄取的參考依據,投檔時,我省將考生的綜合素質評價情況提供給招生高校。具體使用辦法由招生高校在招生章程中予以明確。
七、今年志愿填報和往年相比有什么變化?
一是高校專業增加了選考科目要求。比如,某高校某專業要求考生首選科目為物理,再選科目為化學,只有選科時選擇這兩個科目的考生才能填報,否則為無效志愿。二是平行志愿批次的志愿單位、數量有所調整。志愿填報由以“學校”為單位,變為以“專業(類)+學校”為單位,即一所院校一個招生專業(類)為一個志愿,不再設專業服從調劑選項。實行平行志愿的批次,普通類最多可填報96個志愿,藝術類、體育類最多可填報70個志愿。
八、院校招生專業對再選科目有幾種要求?如何查詢?
高校對再選科目要求分為四種:一是不提再選科目要求的,符合首選科目要求的考生均可報考;二是提出1門再選科目要求的,考生必須選考該科目方可報考,如某校某專業對化學科目提出要求,符合首選科目要求且選考化學的考生才能報考;三是提出2門再選科目要求且均須選考的,考生須同時選考這2門科目方可報考,如某校某專業對化學和生物科目提出要求,符合首選科目要求且同時選考化學和生物的考生才能報考;四是提出2門再選科目要求且只需選考其中1門的,考生選考該2門科目中的任意1門即可報考,如某校某專業要求考生選考化學或生物科目,符合首選科目要求且只要再選科目中有化學或生物的考生即可報考。
考生可以通過以下兩種途徑查詢高校相關選考科目要求:
(1)登錄各高校官方網站進行查詢。
(2)通過《2021年河北省普通高等學校招生計劃》進行查詢。
九、如何理解“專業(類)+學校”志愿模式變化?
我省將在普通類的本科提前批B段、本科批、專科批,藝術類的本科提前批B段、專科提前批部分專業,體育類的本科提前批B段、專科提前批實行”專業(類)+學校”的平行志愿,與原來以學校為單位的志愿模式相比發生了變化。
一是填報的基本單位發生了變化。志愿填報由以“學校”為單位,變為以“專業(類)+學校”為單位,即一所院校一個招生專業(類)為一個志愿。比如原來志愿模式下,要報考北京大學的數學、物理、化學3個專業,只需填報在北京大學1個志愿單位下即可,新的志愿模式下,則需要填報北京大學+數學、北京大學+物理、北京大學+化學3個志愿單位。
二是志愿數量發生了變化。新的志愿模式下,普通類每次最多可填報96個志愿,藝術類、體育類每次最多可填報70個志愿。
三是增加了選考科目要求。改革前考生填報志愿時,同一個科類,考生基本可以填報擬報考學校的所有專業(有特殊要求的除外)。改革后,考生填報志愿須符合擬報考學校專業的選考科目要求,不符合要求的不能填報。
四是不再設專業服從調劑選項。以前的志愿模式下,部分考生因不服從院校的專業調劑,而被投檔院校退檔。”專業(類)+學校”模式取消了專業調劑,考生不必擔心被調劑到不喜歡的專業,也不用擔心出現“因不服從專業調劑而退檔”的情況,但需要注意的是,有些專業對身體條件、語種等另做要求,考生即便被投檔,高校也可能因考生不符合招生章程中的規定而退檔。
下面以普通類本科批志愿設置為例,對比一下改革前和改革后志愿設置的變化:
十、“專業(類)+學校”平行志愿投檔原則是什么?
“專業(類)+學校”平行志愿投檔原則為“分數優先、遵循志愿、一次投檔、不再補檔”,將控制線上未錄取的有志愿考生,結合高校各專業(類)要求,依據高校調檔比例確定投檔考生數,普通類按高考文化總成績(含政策性加分),藝術類、體育類按綜合成績,從高分到低分排序,遵循考生的志愿順序依次投檔,由高校擇優錄取。
具體地講,就是先從排在第一位的考生開始,計算機根據該生填報的1、2、3、4、5……等多個平行志愿,從第1個志愿開始依次檢索,如果第1個志愿有空額就投檔,如已滿額,則檢索第2個志愿,依此類推。一旦投檔,該生后面的志愿即失效。然后再檢索排在第二位的考生……直到所有計劃滿額或者沒有符合投檔條件的考生為止。如果某一考生填報的所有志愿都不符合投檔條件,則不能被投檔。
十一、對”專業(類)+學校”平行志愿認識誤區有哪些?
一般來說對平行志愿的認識誤區有三個:
誤區一:認為可以一檔多投或多次投檔。在同一批(段)考生一次可以填報多個學校專業(類)志愿,但在實際投檔過程中,投檔機會最多只有一次,也就是說,即使有多個志愿符合投檔要求,在這些志愿中,只能投檔到排在前面的那一個,而且一旦投檔,其余志愿隨即失效,不再投檔。投檔后因某種原因被退檔,即使后面還有符合投檔條件的志愿,也不再投檔。
誤區二:認為平行志愿沒有先后順序。對考生個人來說,平行志愿是有先后順序的。平行志愿的檢索順序就是考生所填報的1、2、3、4、5……志愿順序,所以考生一定要精心安排志愿順序,把最心儀的志愿填在靠前的位置。
誤區三:認為實行平行志愿后沒有了風險。仍然會有風險,主要有以下三種原因:平行志愿間沒有拉開梯度。如果考生定位不準確,一味追求最理想的高校和專業,志愿填報過高(比如多個平行院校志愿之間沒有拉開梯度),就可能導致所填報的幾個志愿全部落空。填報的志愿過少。如果考生沒有充分利用選擇機會,只填報了較少數量的志愿,就有可能因為高考成績未達到招生院校的投檔線而不能被錄取。考生的外語語種、身體條件等因素不符合高校要求的,都有可能造成退檔,這些因素由高校根據招生需要確定,并在高校招生章程中公布。投檔是以考生成績和志愿順序作為依據的,考生一旦投到某個高校的專業,但不符合高校招生章程中該專業的具體要求,就會造成退檔。因此考生在填報志愿前一定要認真查看高校招生章程。
十二、填報“專業(類)+學校”平行志愿有哪些建議?
一是認真閱讀《2021年河北省普通高等學校招生計劃》,包括院校名稱、辦學性質、專業(專業類)、層次、選考科目要求、學制、學費、計劃數等。
二是分析研究相關數據。受“專業(類)+學校”平行志愿投檔方式的實施以及高校對選考科目的要求等影響,往年的錄取分數和位次將不再具有直觀的參考意義,但從先期改革省份情況看,高校往年錄取數據仍具有一定參考價值。在填報志愿時,考生要依據自己的成績和位次,綜合考慮相關數據信息,特別是往年招生院校各專業的錄取數據,合理選擇擬填報的院校和專業。
三是選擇確定填報意向。考生首先要根據自己的選考科目、成績位次、興趣愛好、身體狀況等因素,認真閱讀院校的招生章程(詳細了解院校招生政策、錄取規則、學科優勢、專業設置、地理位置等方面信息),詳細了解招生院校各專業(類)招生計劃(包括選科要求、身體條件要求、學費標準、外語語種要求等),明確自己立志攻讀的專業范圍和院校范圍。
四是認真研讀高校的招生章程。要特別注意各專業對考生的要求,如身體條件、外語語種、單科成績、綜合素質評價、選考科目要求等是否符合報考條件。
五是合理安排志愿順序。考生要把心儀的志愿填在靠前的位置,志愿之間要有合理梯度,不宜全部填報高分段也不必全部填報低分段志愿。考生應盡量填滿志愿,多一個志愿,可能就多一分錄取機會。
十三、往年的招生錄取數據如何查詢?
一是登錄擬填報院校官方網站進行查詢。
二是通過省教育考試院匯編的《全國普通高校在河北招生錄取分數分布統計2018—2020》《2021年河北省普通高校招生報考指南》和《2021年河北省普通高等學校招生藝術類報考指南》。這些資料收集整理了近三年高校錄取的相關數據和資料。
三是通過河北省高考志愿填報智能參考系統查詢。考生憑使用碼通過河北省招生考試信息服務網(http://www.hebeeb.com/)登錄使用。詳見第三十二條。
十四、對口類志愿填報和往年比有變化嗎,其錄取批次和志愿是怎么設置的?
對口類志愿填報和往年比沒有任何變化。仍分為對口本科批和對口專科批兩個批次,均設1次集中填報志愿和1次征集志愿,實行以“學校”為單位的平行志愿投檔模式,每個志愿可填報5所院校(每所院校設6個專業志愿和1個專業服從調劑選項)。其中,對口本科批隨本科提前批B段填報志愿、錄取,對口專科批隨專科提前批填報志愿、錄取。
考生如被上一批次高校錄取,不能再參加下一批次高校的錄取;如未被上一批次高校錄取,其參加下一批次高校錄取的機會不受影響。
十五、物理科目組合和歷史科目組合各錄取批次及志愿是如何設置的?
可分為普通類、藝術類、體育類,分別進行批次設置,實行平行志愿和順序志愿兩種模式。
(一)普通類。錄取批次分為本科提前批、本科批、專科提前批、專科批等四個批次。一是本科提前批。分為A、B、C三段,每段為一個獨立的小批次,依次進行錄取。其中,本科提前批A段包括有政治考察、面試、體檢等特殊要求的國家專項計劃、軍隊、公安、司法等本科專業。實行以學校為單位的順序志愿模式。設1次集中填報志愿和1次征集志愿,每次可填報1所學校,每所學校設6個專業志愿和1個專業服從調劑選項。本科提前批B段包括除本科提前批A段以外的其他國家專項計劃、公費師范生和免費醫學定向生等本科專業。實行以“專業(類)+學校”為單位的平行志愿模式,一所院校一個招生專業(類)為一個志愿。設1次集中填報志愿和1次征集志愿,每次最多可填報96個志愿。本科提前批C段包括高水平運動隊、高水平藝術團、高校專項計劃、定向就業招生等特殊類型招生本科專業。實行以學校為單位的順序志愿模式。只設1次集中填報志愿,不進行志愿征集,可填報1所學校,設6個專業志愿和1個專業服從調劑選項。二是本科批。包括未列入本科提前批的普通類本科專業、地方專項計劃、本科預科班(包括少數民族預科班、邊防軍人子女預科班)等。實行以“專業(類)+學校”為單位的平行志愿模式,一所院校一個招生專業(類)為一個志愿。設1次集中填報志愿和1次征集志愿,每次最多可填報96個志愿。三是專科提前批。包括有面試、體檢等特殊要求的公安、司法等專科專業。實行以學校為單位的順序志愿模式。設1次集中填報志愿和1次征集志愿,每次可填報1所學校,每所學校設6個專業志愿和1個專業服從調劑選項。四是專科批。包括未列入專科提前批的普通類專科專業。實行以“專業(類)+學校”為單位的平行志愿模式,一所院校一個招生專業(類)為一個志愿。設1次集中填報志愿和2次征集志愿,每次最多可填報96個志愿。
(二)藝術類。藝術類招生分本科提前批和專科提前批兩個批次。每次志愿填報時(集中填報志愿或征集志愿),同一批次只能選擇一個科類填報,各科類不能同時兼報。一是本科提前批。分為A、B、C三段,每段為一個獨立的小批次,依次進行錄取。本科提前批A段包括教育部批準的獨立設置藝術高校、參照執行高校的有關藝術類本科專業,原“211工程”高校的藝術類校考本科專業,有特殊要求無法實行平行志愿投檔的藝術類統考和校際聯考等本科專業。實行以學校為單位的順序志愿模式。設1次集中填報志愿和1次征集志愿,每次可填報1所學校,每所學校設6個專業志愿和1個專業服從調劑選項。本科提前批B段包括使用河北省藝術統考或校際聯考成績作為專業成績進行錄取,并執行我省平行志愿統一投檔原則的藝術類本科專業。實行以“專業(類)+學校”為單位的平行志愿模式,一所院校一個招生專業(類)為一個志愿。設1次集中填報志愿和1次征集志愿,每次最多可填報70個志愿。本科提前批C段包括除本科提前批A段、本科提前批B段以外的其他藝術類本科專業。實行以學校為單位的順序志愿模式。設1次集中填報志愿和1次征集志愿,每次可填報1所學校,每所學校設6個專業志愿和1個專業服從調劑選項。二是專科提前批。包括藝術類專科專業。分平行志愿和順序志愿兩種模式。其中,使用河北省藝術統考或校際聯考成績作為專業成績進行錄取的,實行以“專業(類)+學校”為單位的平行志愿模式,一所院校一個招生專業(類)為一個志愿,設1次集中填報志愿和1次征集志愿,每次最多可填報70個志愿;使用藝術校考成績作為專業成績進行錄取的,實行以學校為單位的順序志愿模式,設1次集中填報志愿和1次征集志愿,每次可填報1所學校,每所學校設6個專業志愿和1個專業服從調劑選項。
(三)體育類。體育類招生分本科提前批和專科提前批兩個批次。一是本科提前批。分為A、B兩段,每段為一個獨立的小批次,依次進行錄取。本科提前批A段包括河北體育學院少數民族傳統體育項目。實行以學校為單位的順序志愿模式。設1次集中填報志愿和1次征集志愿。本科提前批B段包括使用河北省普通體育類專業考試成績作為專業成績進行錄取的體育類本科專業。實行以“專業(類)+學校”為單位的平行志愿模式。設1次集中填報志愿和1次征集志愿。一所院校一個招生專業(類)為一個志愿,每次最多可填報70個志愿。二是專科提前批。包括使用河北省普通體育類專業考試成績作為專業成績進行錄取的體育類專科專業。實行以“專業(類)+學校”為單位的平行志愿模式,一所院校一個招生專業(類)為一個志愿。設1次集中填報志愿和1次征集志愿,每次最多可填報70個志愿。
考生如被上一批次(段)高校錄取,不能再參加下一批次(段)高校的錄取;如未被上一批次(段)高校錄取,其參加下一批次(段)高校錄取的機會不受影響。
十六、藝術類、體育類考生的綜合成績是如何構成的?
藝術類、體育類綜合成績的構成:
(1)美術類、音樂類(含聲樂類和器樂類)、舞蹈類、書法學、服裝表演類:綜合成績=高考文化總成績(含政策性加分)×0.3+(專業成績÷專業滿分)×750×0.7,結果四舍五入保留3位小數。其中,美術類專業滿分300分,音樂類(含聲樂類和器樂類)、舞蹈類、書法學專業滿分均為200分,服裝表演類專業滿分100分。
(2)戲劇與影視學類、播音與主持藝術:綜合成績=高考文化總成績(含政策性加分)×0.7+(專業成績÷專業滿分)×750×0.3,結果四舍五入保留3位小數。戲劇與影視學類、播音與主持藝術專業滿分均為200分。
(3)體育類:綜合成績=高考文化總成績(含政策性加分)×0.3+(專業成績÷專業滿分)×750×0.7,結果四舍五入保留3位小數。體育類專業滿分400分。
十七、藝術類、體育類考生可以兼報普通類專業嗎?
藝術類和體育類考生,可以兼報普通類專業,但每次志愿填報時(集中填報志愿或征集志愿),同一批次(段)只能選擇一個類別填報。
十八、填報軍隊院校志愿應注意哪些事項?
今年軍隊院校招生的變化主要有兩點,一是考生在公布高考成績后,于6月26日12時至6月27日12時填報軍隊院校志愿。二是根據教育部、中央軍委政治工作部、中央軍委訓練管理部《關于做好2021年軍隊院校招收普通高中畢業生工作的通知》(軍訓〔2021〕112號)精神,報考軍隊院校考生高中階段體質測試成績需達到及格以上(即普通高中學業水平合格性考試體育與健康科目成績為“合格”)。應屆生體育與健康科目成績由省中考中心提供,合格考生無需提供相關成績證明,考生可通過河北省普通中等教育考試服務中心網站(https://www.hebeixk.com)查詢本人的體育與健康科目成績是否合格。往屆生持學考成績合格證復印件,或通過河北省普通中等教育考試服務中心網站(https://www.hebhk.com)打印的含有本人體育與健康科目成績合格的截圖,或畢業學校出具的加蓋學校公章的相關成績合格證明,在考生參加軍檢時向相應軍檢站提交。
軍隊院校招生計劃安排在本科提前批A段,實行順序志愿投檔,設1次集中填報志愿和1次征集志愿,考生每次可填報1所學校,每所學校設6個專業志愿和1個專業服從調劑選項。
已完成軍隊院校(專業)志愿填報的考生,仍可在規定時間填報包括本科提前批A段在內的普通院校(專業)志愿。其中,通過省軍區戰備建設局組織的政治考核和軍檢合格的考生,其軍隊院校(專業)志愿生效,本科提前批A段的普通院校(專業)志愿無效;未通過省軍區戰備建設局組織的政治考核或軍檢不合格的考生,其軍隊院校(專業)志愿無效,本科提前批A段的普通院校(專業)志愿生效。
陸軍工程大學安排在本科提前批A段的定向人防計劃以及空軍軍醫大學和海軍軍醫大學安排在本科批的普通類招生計劃,考生不用參加政治考核和軍事職業適應性檢測,入學后無軍籍,不享受軍官學員相關待遇。這兩類計劃隨相應批次普通類專業進行填報,不屬于6月26日12時至6月27日12時軍隊院校志愿填報范圍。
具體要求詳見軍隊院校招生相關文件,或及時關注省教育考試院官方網站查詢有關招生辦法。
十九、填報公安院校志愿應注意哪些事項?
報考公安院校的考生須參加由省公安廳組織的政治考察、面試、體檢和體能測評且合格。公安院校招生計劃安排在本科提前批A段和專科提前批,實行順序志愿投檔,設1次集中填報志愿和1次征集志愿,每批(段)每次只能填報1所院校,每所學校設6個專業志愿和1個專業服從調劑選項。公安院校的國家專項計劃與其他公安類本科專業都編列在本科提前批A段,計劃性質均為非定向,這兩類計劃分開編列,公安類的國家專項計劃統一編排在所有普通類公安計劃之后,同時具有相應資格的考生只能選擇一類填報。
具體要求詳見公安院校招生相關文件,或及時關注省教育考試院官方網站查詢有關招生辦法。
二十、如何填報定向就業招生志愿?
定向就業招生計劃單列,實行順序志愿投檔,每個批次(段)只能填報1所院校志愿。解放軍陸軍工程大學定向人防計劃安排在本科提前批A段,清華大學、東北大學秦皇島分校的定向就業計劃安排在本科提前批C段,詳見當年招生計劃。
二十一、高水平運動隊招生如何填報志愿?
報考高水平運動隊的考生,須通過招生院校考核合格且經教育部“陽光高考”信息平臺公示無異議后方可填報。高水平運動隊在本科提前批C段錄取,實行順序志愿,設1次集中填報志愿,不進行志愿征集。可填報1所學校,設6個專業志愿和1個專業服從調劑選項。
二十二、高水平藝術團招生如何填報志愿?
報考高水平藝術團的考生,須通過招生院校考核合格且經教育部“陽光高考”信息平臺公示無異議后方可填報。高水平藝術團在本科提前批C段錄取,實行順序志愿,設1次集中填報志愿,不進行志愿征集。可填報1所學校,設6個專業志愿和1個專業服從調劑選項。
二十三、國家專項計劃招生如何填報志愿?
國家專項計劃志愿安排在本科提前批A段和B段。其中,有政治考察、面試、體檢等特殊要求的國家專項計劃安排在本科提前批A段,其他國家專項安排在本科提前批B段。通過國家專項計劃資格審核的考生方可填報。
本科提前批A段的國家專項計劃實行順序志愿,設集中填報志愿和征集志愿,每次只能填報1所學校,設6個專業志愿和1個專業服從調劑選項。本科提前批B段的國家專項計劃實行平行志愿,設集中填報志愿和征集志愿,每次最多可填報96個志愿,與免費醫學定向生、公費師范生同批一起填報,同批一起錄取。
二十四、高校專項計劃招生如何填報志愿?
高校專項計劃列在本科提前批C段,實行順序志愿,設1次集中填報志愿,不進行志愿征集,可填報1所學校,設6個專業志愿和1個專業服從調劑選項。經有關高校考核合格且在教育部“陽光高考”信息平臺公示無異議的考生方可填報。
二十五、地方專項計劃招生如何填報志愿?
地方專項計劃安排在本科批,設集中填報志愿和征集志愿,與其他普通類學校同批一起填報,同批一起錄取,每次最多可填報96個志愿。通過地方專項計劃資格審核的考生方可填報。
二十六、如何填報公費師范生(含優師專項)志愿?
教育部直屬部分師范院校和我省部分師范院校繼續安排公費師范生招生。普通類公費師范生安排在本科提前批B段。今年國家在我省新增優師專項,該計劃屬于公費師范生范疇,是為中西部欠發達地區定向培養教師的專項計劃,面向全省招生,分為國家優師專項(部委屬師范大學承擔)和地方優師專項(河北師范大學和河北民族師范學院承擔),面向我省特困片區縣和扶貧重點縣就業。普通類的優師專項列在普通類本科提前批B段,編列到公費師范生類,單列院校代號,院校名稱后單獨標注“優師專項”,與公費師范生、免費醫學定向生、國家專項計劃一起實行平行志愿。除以上普通類公費師范生(含優師專項)招生之外,藝術、體育、高校專項計劃等招生類型的公費師范生(含優師專項)安排在相應類別和批次。考生被公費師范生(含優師專項)錄取后須簽訂協議,詳情請咨詢有關院校。
二十七、如何填報預科班志愿?
預科班招生包括少數民族本科預科班和邊防軍人子女預科班。含在普通本科批內,設集中填報志愿和征集志愿,與其他普通類學校同批一起填報,同批一起錄取,每次最多可填報96個志愿。
其中少數民族本科預科班只招收少數民族考生,邊防軍人子女預科班只招收通過邊防軍人子女資格審查的考生。
二十八、強基計劃有關注意事項?
報考強基計劃的考生無需在我省志愿填報系統填報強基計劃志愿,按有關試點高校《招生簡章》的要求報考,由高校確定錄取結果。我省根據高校確定的本省擬錄取考生名單,在提前批錄取開始前辦理錄取備案手續。強基計劃考生可參加我省其他各類高考志愿正常填報,如被強基計劃錄取,將不再參加其他各類型投檔和錄取工作。
二十九、投檔時,考生成績相同時如何排序?
普通類:當遇到多名考生高考文化總成績(含政策性加分)相同時,依次按語文數學兩門成績之和、語文數學兩門中的單科最高成績、外語單科成績、首選科目單科成績、再選科目單科最高成績、再選科目單科次高成績由高到低排序投檔;如仍相同,比較考生志愿順序,順序在前者優先投檔,志愿順序相同則全部投檔,是否錄取由高校決定。
軍隊院校、綜合評價招生等有特殊投檔要求的,按照有關文件執行。
藝術類、體育類:實行平行志愿模式的批次,當遇到多名考生綜合成績相同時,依次按高考文化總成績(含政策性加分)、語文數學兩門成績之和、語文數學兩門中的單科最高成績、外語單科成績、首選科目單科成績、再選科目單科最高成績、再選科目單科次高成績由高到低排序投檔;如仍相同,比較考生志愿順序,順序在前者優先投檔,志愿順序相同則全部投檔,是否錄取由高校決定。
三十、填報志愿時考生如何對待優惠加分政策?
對具備加分資格的考生,我省按照加分后的分數投檔。具備國家加分項目資格的考生可在其統考成績總分基礎上增加分值投檔,由高校審查決定是否錄取。具備我省地方加分項目資格的考生,加分范圍只適用于我省高校省內招生,在考生文化統考成績總分基礎上增加分值投檔,由高校審查決定是否錄取。對于同時符合多種加分條件的考生,投檔時,報考省外院校的使用國家加分最高一項,報考河北省院校的使用國家加分與省內加分中最高一項。根據教育部有關規定,所有高考加分項目及分值均不得用于高校不安排分省招生計劃的藝術類專業、高水平藝術團、高水平運動隊、高校專項計劃等招生項目。錄取時是否考慮加分因素,按院校的有關規定執行。由于部分院校不承認或部分承認優惠加分政策,因此,享有優惠加分政策投檔的考生,有可能因實際文化成績較低而被院校退檔。請考生在填報志愿前,務必認真閱讀有關院校《招生章程》或向院校咨詢。
三十一、志愿填報時間截止后還可以補報志愿嗎?
不接受任何形式的補報志愿。志愿填報系統將按照規定的志愿填報時間準時開放和關閉,考生須按照有關操作流程,在規定時間內完成志愿填報。
三十二、什么是河北省高考志愿填報智能參考系統?有哪些功能?
該系統是集專業測試、信息查詢、志愿智能選擇等功能的一站式輔助咨詢服務系統。系統將在成績公布后開放使用,可為河北省普通高校招生普通類本科專業(不含本科提前批)志愿填報提供參考。考生只需“錄信息”“挑專業”“排順序”“導報表”四步,系統即可給出志愿填報參考建議。考生憑使用碼通過河北省招生考試信息服務網(http://www.hebeeb.com/)登錄使用。
6月23日后為模擬試用期,模擬數據在系統正式上線后清除。6月25日成績公布后,系統將正式上線。
技術服務熱線:13231165075/15373010897
(河北省教育考試院)
*請認真填寫需求信息,我們會在24小時內與您取得聯系。