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 日本一区二区视频在线观看,97av麻豆蜜桃一区二区,一二三四视频日本高清

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          探探各個(gè)微前端框架

          探探各個(gè)微前端框架

          前端架構(gòu)是為了在解決單體應(yīng)用在一個(gè)相對(duì)長的時(shí)間跨度下,由于參與的人員、團(tuán)隊(duì)的增多、變遷,從一個(gè)普通應(yīng)用演變成一個(gè)巨石應(yīng)用(Frontend Monolith)后,隨之而來的應(yīng)用不可維護(hù)的問題。這類問題在企業(yè)級(jí) Web 應(yīng)用中尤其常見。
          微前端框架內(nèi)的各個(gè)應(yīng)用都支持獨(dú)立開發(fā)部署、不限技術(shù)框架、支持獨(dú)立運(yùn)行、應(yīng)用狀態(tài)隔離但也可共享等特征。
          本文會(huì)從框架的應(yīng)用隔離實(shí)現(xiàn)方案、實(shí)戰(zhàn)、優(yōu)缺點(diǎn)三個(gè)方面探一探各個(gè)框架。幫助大家了解各個(gè)框架是如何使用,如何運(yùn)行,從而能選出適合自己項(xiàng)目的微前端方案。

          iframe

          在沒有各大微前端解決方案之前,iframe是解決這類問題的不二之選,因?yàn)?span style="color: #9654B5; --tt-darkmode-color: #9654B5;">iframe提供了瀏覽器原生的硬隔離方案,不論是樣式隔離、js 隔離這類問題統(tǒng)統(tǒng)都能被完美解決。
          但他的最大問題也在于他的隔離性無法被突破,導(dǎo)致應(yīng)用間上下文無法被共享,隨之帶來的開發(fā)體驗(yàn)、產(chǎn)品體驗(yàn)的問題:

          1. url 不同步,瀏覽器刷新 iframe url 狀態(tài)丟失、后退前進(jìn)按鈕無法使用。
          2. UI 不同步,DOM 結(jié)構(gòu)不共享,彈窗只能在iframe內(nèi)部展示,無法覆蓋全局
          3. 全局上下文完全隔離,內(nèi)存變量不共享,iframe 內(nèi)外系統(tǒng)的通信、數(shù)據(jù)同步等需求,主應(yīng)用的 cookie 要透?jìng)鞯礁蛎疾煌淖討?yīng)用中實(shí)現(xiàn)免登效果。
          4. ,每次子應(yīng)用進(jìn)入都是一次瀏覽器上下文重建、資源重新加載的過程。

          single-spa

          目前(2024年4月)github star 13k
          Single-spa(https://github.com/single-spa/single-spa) 是最早的微前端框架,兼容多種前端技術(shù)棧;是一個(gè)將多個(gè)單頁面應(yīng)用聚合為一個(gè)整體應(yīng)用的 JavaScript 微前端框架;
          簡(jiǎn)單來說就是一個(gè)聚合,使用這個(gè)庫可以讓你的應(yīng)用可以 使用多個(gè)不同的技術(shù)棧(vue、react、angular等等)進(jìn)行同步開發(fā),最后使用一個(gè)公用的路由去實(shí)現(xiàn)完美的切換;

          實(shí)現(xiàn)方案

          Single-spa 實(shí)現(xiàn)了一套生命周期,開發(fā)者需要在相應(yīng)的時(shí)機(jī)自己去加載對(duì)應(yīng)的子應(yīng)用。
          它做的事情就是
          注冊(cè)子應(yīng)用、監(jiān)聽 URL 變化,然后加載對(duì)應(yīng)的子應(yīng)用js,執(zhí)行對(duì)應(yīng)子應(yīng)用的生命周期流程。

          1. 提供registerApplication方法,用來注冊(cè)子應(yīng)用列表。
          2. 提供了activeWhen,由開發(fā)者指定路由滿足條件時(shí),激活(掛載)子應(yīng)用的js、css。
          3. js隔離由single-spa-leaked-globals實(shí)現(xiàn),本質(zhì)上就是在 mount A 子應(yīng)用時(shí),正常添加全局變量,比如 jQuery 的 $, lodash 的 _。在 unmount A 子應(yīng)用時(shí),用一個(gè)對(duì)象記錄之前給 window 添加的全局變量,并把 A 應(yīng)用里添加 window 的變量都刪掉。下一次再 mount A 應(yīng)用時(shí),把記錄的全局變量重新加回來就好了。
          4. css隔離:子應(yīng)用和子應(yīng)用之間通過single-spa-css插件提供的css生命周期函數(shù),做到子應(yīng)用mount時(shí)加載css,子應(yīng)用unmount時(shí)將css也unmount掉;而主應(yīng)用與子應(yīng)用之間可以通過PostCSSPrefix Selector 給樣式自動(dòng)加前綴的方式,或者Shadow DOM 的形式去解決。

          single-spa實(shí)戰(zhàn)

          1. 主應(yīng)用入口文件:

          主要通過single-spa提供的registerApplication方法注冊(cè)子應(yīng)用,子應(yīng)用需要指定加載子應(yīng)用的方法、和路由條件。

          import Vue from 'vue'
          import App from './App.vue'
          import router from './router'
          import { registerApplication, start } from 'single-spa'
          
          Vue.config.productionTip=false
          
          // 遠(yuǎn)程加載子應(yīng)用
          function createScript(url) {
            return new Promise((resolve, reject)=> {
              const script=document.createElement('script')
              script.src=url
              script.onload=resolve
              script.onerror=reject
              const firstScript=document.getElementsByTagName('script')[0]
              firstScript.parentNode.insertBefore(script, firstScript)
            })
          }
          
          // 記載函數(shù),返回一個(gè) promise
          function loadApp(url, globalVar) {
            // 支持遠(yuǎn)程加載子應(yīng)用
            return async ()=> {
              await createScript(url + '/js/chunk-vendors.js')
              await createScript(url + '/js/app.js')
              // 這里的return很重要,需要從這個(gè)全局對(duì)象中拿到子應(yīng)用暴露出來的生命周期函數(shù)
              return window[globalVar]
            }
          }
          
          // 子應(yīng)用列表
          const apps=[
            {
              // 子應(yīng)用名稱
              name: 'app1',
              // 子應(yīng)用加載函數(shù),是一個(gè)promise
              app: loadApp('http://localhost:8081', 'app1'),
              // 當(dāng)路由滿足條件時(shí)(返回true),激活(掛載)子應(yīng)用
              activeWhen: location=> location.pathname.startsWith('/app1'),
              // 傳遞給子應(yīng)用的對(duì)象
              customProps: {}
            },
            {
              name: 'app2',
              app: loadApp('http://localhost:8082', 'app2'),
              activeWhen: location=> location.pathname.startsWith('/app2'),
              customProps: {}
            },
            {
              // 子應(yīng)用名稱
              name: 'app3',
              // 子應(yīng)用加載函數(shù),是一個(gè)promise
              app: loadApp('http://localhost:3000', 'app3'),
              // 當(dāng)路由滿足條件時(shí)(返回true),激活(掛載)子應(yīng)用
              activeWhen: location=> location.pathname.startsWith('/app3'),
              // 傳遞給子應(yīng)用的對(duì)象,這個(gè)很重要,該配置告訴react子應(yīng)用自己的容器元素是什么,這塊兒和vue子應(yīng)用的集成不一樣,官網(wǎng)并沒有說這部分,或者我沒找到,是通過看single-spa-react源碼知道的
              customProps: {
                domElement: document.getElementById('microApp'),
                // 添加 name 屬性是為了兼容自己寫的lyn-single-spa,原生的不需要,當(dāng)然加了也不影響
                name: 'app3'
              }
            }
          ]
          
          // 注冊(cè)子應(yīng)用
          for (let i=apps.length - 1; i >=0; i--) {
            registerApplication(apps[i])
          }
          
          new Vue({
            router,
            mounted() {
              // 啟動(dòng)
              start()
            },
            render: h=> h(App)
          }).$mount('#app')
          
          

          2. 子應(yīng)用導(dǎo)出文件

          • 子應(yīng)用需要安裝single-spa-react或者single-spa-vue,
          • 將子應(yīng)用傳遞給single-spa-react,得到子應(yīng)用運(yùn)行的生命周期,
          • 子應(yīng)用將生命周期導(dǎo)出到全局,
          • 在主應(yīng)用可以獲取子應(yīng)用的生命周期函數(shù)
          import React from 'react';
          import ReactDOM from 'react-dom';
          import './index.css'
          import { BrowserRouter, Link, Route } from 'react-router-dom'
          import singleSpaReact from 'single-spa-react'
          
          // 子應(yīng)用獨(dú)立運(yùn)行
          if (!window.singleSpaNavigate) {
            ReactDOM.render(rootComponent(), document.getElementById('root'))
          }
          
          // 生命周期a
          const reactLifecycles=singleSpaReact({
            React,
            ReactDOM,
            rootComponent,
            errorBoundary(err, info, props) {
              return <div>
                This renders when a catastrophic error occurs
              </div>
            }
          })
          
          // 這里和vue不一樣,props必須向下傳遞
          export const bootstrap=async props=> {
            console.log('app3 bootstrap');
            return reactLifecycles.bootstrap(props)
          }
          export const mount=async props=> {
            console.log('app3 mount');
            return reactLifecycles.mount(props);
          }
          export const unmount=async props=> {
            console.log('app3 unmount');
            return reactLifecycles.unmount(props)
          }
          
          // 根組件
          function rootComponent() {
            return <React.StrictMode>
              <BrowserRouter>
                <div>
                  <Link to="/app3">Home</Link> |
                    <Link to="/app3/about"> About</Link>
                  <Route exact path="/app3" component={Home} />
                  <Route exact path="/app3/about" component={About} />
                </div>
              </BrowserRouter>
            </React.StrictMode>
          }
          
          // home 組件
          function Home() {
            return <div>
              <h1>app3 home page</h1>
            </div>
          }
          
          // about 組件
          function About() {
            return <div>
              <h1>app3 about page</h1>
            </div>
          }
          

          3. 打包配置

          將子應(yīng)用導(dǎo)出模式設(shè)置為umd

          const package=require('./package.json')
          module.exports={
            // 告訴子應(yīng)用在這個(gè)地址加載靜態(tài)資源,否則會(huì)去基座應(yīng)用的域名下加載
            publicPath: '//localhost:8082',
            // 開發(fā)服務(wù)器
            devServer: {
              port: 8082
            },
            configureWebpack: {
              // 導(dǎo)出umd格式的包,在全局對(duì)象上掛載屬性package.name,基座應(yīng)用需要通過這個(gè)全局對(duì)象獲取一些信息,比如子應(yīng)用導(dǎo)出的生命周期函數(shù)
              output: {
                // library的值在所有子應(yīng)用中需要唯一
                library: package.name,
                libraryTarget: 'umd'
              }
            }
          }
          

          4. 預(yù)覽


          可以看到它是動(dòng)態(tài)加載的子應(yīng)用的js,并執(zhí)行js,將內(nèi)容渲染到了主應(yīng)用的盒子內(nèi)。

          框架優(yōu)缺點(diǎn)

          優(yōu)點(diǎn):

          • 敏捷性 - 獨(dú)立開發(fā)、獨(dú)立部署,微應(yīng)用倉庫獨(dú)立,前后端可獨(dú)立開發(fā),部署完成后主框架自動(dòng)完成同步更新;
          • 技術(shù)棧無關(guān),主框架不限制接入應(yīng)用的技術(shù)棧,微應(yīng)用具備完全自主權(quán);
          • 增量升級(jí),在面對(duì)各種復(fù)雜場(chǎng)景時(shí),我們通常很難對(duì)一個(gè)已經(jīng)存在的系統(tǒng)做全量的技術(shù)棧升級(jí)或重構(gòu),而微前端是一種非常好的實(shí)施漸進(jìn)式重構(gòu)的手段和策略

          缺點(diǎn)

          • 需要自己去加載子應(yīng)用
          • 不支持 Javascript 沙箱隔離,需要自己去使用single-spa-leaked-globals之類的庫去隔離
          • 不支持css隔離,需要自己使用single-spa-css庫或者postcss等去解決樣式?jīng)_突問題
          • 無法預(yù)加載

          qiankun

          目前(2024年4月) github star 15.4k
          阿里的
          qiankun 是一個(gè)基于 single-spa 的微前端實(shí)現(xiàn)庫,孵化自螞蟻金融,幫助大家能更簡(jiǎn)單、無痛的構(gòu)建一個(gè)生產(chǎn)可用微前端架構(gòu)系統(tǒng)。

          實(shí)現(xiàn)方案

          • single-spa是基于js-entry方案,而qiankun 是基于html-entry 及沙箱設(shè)計(jì),使得微應(yīng)用的接入 像使用 iframe 一樣簡(jiǎn)單
          • 主應(yīng)用監(jiān)聽路由,加載對(duì)應(yīng)子應(yīng)用的html,掛載到主應(yīng)用的元素內(nèi),然后解析子應(yīng)用的html,從中分析出css、js再去沙盒化后加載執(zhí)行,最終將子應(yīng)用的內(nèi)容渲染出來。
          • qiankun實(shí)現(xiàn)樣式隔離有兩種模式可供開發(fā)者選擇:
            • strictStyleIsolation 這種模式下 qiankun 會(huì)為每個(gè)微應(yīng)用的容器包裹上一個(gè) shadow dom 節(jié)點(diǎn),從而確保微應(yīng)用的樣式不會(huì)對(duì)全局造成影響。
            • experimentalStyleIsolation 當(dāng) experimentalStyleIsolation 被設(shè)置為 true 時(shí),qiankun 會(huì)改寫子應(yīng)用所添加的樣式,會(huì)為所有樣式規(guī)則增加一個(gè)特殊的選擇器規(guī)則,來限定其影響范圍
          • qiankun實(shí)現(xiàn)js隔離,采用了兩種沙箱,分別為基于Proxy實(shí)現(xiàn)的沙箱快照沙箱,當(dāng)瀏覽器不支持Proxy會(huì)降級(jí)為快照沙箱

          Proxy沙箱機(jī)制:

          // 偽代碼
          class ProxySandbox {
              constructor() {
                  const rawWindow=window;
                  const fakeWindow={}
                  const proxy=new Proxy(fakeWindow, {
                      set(target, p, value) {
                          target[p]=value;
                          return true
                      },
                      get(target, p) {
                          return target[p] || rawWindow[p];
                      }
                  });
                  this.proxy=proxy
              }
          }
          let sandbox1=new ProxySandbox();
          let sandbox2=new ProxySandbox();
          window.a=1;
          // 偽代碼
          ((window)=> {
              window.a='hello';
              console.log(window.a) // hello
          })(sandbox1.proxy);
          ((window)=> {
              window.a='world';
              console.log(window.a) // world
          })(sandbox2.proxy);
          

          快照沙箱

          // 偽代碼
          class SnapshotSandbox {
              constructor() {
                  this.proxy=window; 
                  this.modifyPropsMap={}; // 修改了那些屬性
                  this.active(); // 調(diào)用active保存主應(yīng)用window快照
              }
          
              /**1. 初始化時(shí),在子應(yīng)用即將mount前,先調(diào)用active,保存當(dāng)前主應(yīng)用的window快照*/
              active() {
                  this.windowSnapshot={}; // window對(duì)象的快照
                  for (const prop in window) {
                      if (window.hasOwnProperty(prop)) {
                          // 將window上的屬性進(jìn)行拍照
                          this.windowSnapshot[prop]=window[prop];
                      }
                  }
                  Object.keys(this.modifyPropsMap).forEach(p=> {
                      window[p]=this.modifyPropsMap[p];
                  });
              }
          
              /**
              * 子應(yīng)用卸載時(shí),遍歷當(dāng)前子應(yīng)用的window屬性,和主應(yīng)用的window快照做對(duì)比
              * 如果不一致,做兩步操作 
              *     1. 保存 不一致的window屬性,
              *     2. 還原window
              */
              inactive() {
                  for (const prop in window) { // diff 差異
                      if (window.hasOwnProperty(prop)) {
                          // 將上次拍照的結(jié)果和本次window屬性做對(duì)比
                          if (window[prop] !==this.windowSnapshot[prop]) {
                              // 保存修改后的結(jié)果
                              this.modifyPropsMap[prop]=window[prop]; 
                              // 還原window
                              window[prop]=this.windowSnapshot[prop]; 
                          }
                      }
                  }
              }
          }
          

          qiankun實(shí)戰(zhàn)

          1. 主應(yīng)用入口文件

          初始化主應(yīng)用,并注冊(cè)子應(yīng)用

          主應(yīng)用入口文件初始化應(yīng)用,注冊(cè)子應(yīng)用,注冊(cè)子應(yīng)用時(shí)支持傳入子應(yīng)用列表, 注冊(cè)子應(yīng)用時(shí)需要指明以下幾個(gè)主要參數(shù):

          1. name: 微應(yīng)用的名稱,微應(yīng)用之間必須確保唯一
          2. entry: 子應(yīng)用的訪問鏈接。主應(yīng)用會(huì)加載整個(gè)頁面,例如https://qiankun.umijs.org/guide/
          3. container:需要掛載子應(yīng)用的DOM元素
          4. loader: 子應(yīng)用未加載時(shí)的界面,一般為loading
          5. activeRule: 路由匹配規(guī)則

          開啟子應(yīng)用start(options)

          options.prefetch此時(shí)可以選擇是否預(yù)加載子應(yīng)用。
          options.sandbox默認(rèn)情況下的沙箱可以確保單實(shí)例場(chǎng)景子應(yīng)用之間的樣式隔離,但是無法確保主應(yīng)用跟子應(yīng)用、或者多實(shí)例場(chǎng)景的子應(yīng)用樣式隔離。qiankun提供了另外兩種方式的隔離,供開發(fā)者選擇:

          • strictStyleIsolation: 當(dāng)配置為 { strictStyleIsolation: true } 時(shí)表示開啟嚴(yán)格的樣式隔離模式。這種模式下 qiankun 會(huì)為每個(gè)微應(yīng)用的容器包裹上一個(gè) shadow dom 節(jié)點(diǎn),從而確保微應(yīng)用的樣式不會(huì)對(duì)全局造成影響。
          • experimentalStyleIsolation:當(dāng) {experimentalStyleIsolation: true} 被設(shè)置,qiankun 會(huì)改寫子應(yīng)用所添加的樣式為所有樣式規(guī)則增加一個(gè)特殊的選擇器規(guī)則來限定其影響范圍。
          import { registerMicroApps, start, initGlobalState } from 'qiankun';
          
          registerMicroApps([
            {
              name: 'react app', // app name registered
              entry: '//localhost:7100',
              container: '#yourContainer',
              activeRule: '/yourActiveRule',
            },
            {
              name: 'vue app',
              entry: { scripts: ['//localhost:7100/main.js'] },
              container: '#yourContainer2',
              activeRule: '/yourActiveRule2',
            },
          ]);
          
          // 通訊
          const { onGlobalStateChange, setGlobalState }=initGlobalState({
            user: 'qiankun',
          });
          
          onGlobalStateChange((value, prev)=> console.log('[onGlobalStateChange - master]:', value, prev));
          
          setGlobalState({
            ignore: 'master',
            user: {
              name: 'master',
            },
          });
          
          /**
           * 設(shè)置默認(rèn)進(jìn)入的子應(yīng)用
           */
          setDefaultMountApp('/react16');
          
          /**
           * 啟動(dòng)應(yīng)用
           */
          start({
            prefetch: true, // 預(yù)加載子應(yīng)用
            sandbox:{
               strictStyleIsolation: true, //  shadow dom的方式實(shí)現(xiàn)樣式隔離
              // experimentalStyleIsolation: true, //添加特殊的選擇器的方式實(shí)現(xiàn)樣式隔離
            }
          });
          
          runAfterFirstMounted(()=> {
            console.log('[MainApp] first app mounted');
          });
          
          

          2. 子應(yīng)用導(dǎo)出生命周期鉤子

          子應(yīng)用需要在自己的入口 js導(dǎo)出 bootstrap、mount、unmount 三個(gè)生命周期鉤子,以供主應(yīng)用在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用。

          import React from 'react';
          import ReactDOM from 'react-dom';
          import App from './App';
          import * as serviceWorker from './serviceWorker';
          
          function render(props) {
            const { container }=props;
            ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
          }
          
          /**
          * 和主應(yīng)用通訊
          */
          function storeTest(props) {
            props.onGlobalStateChange((value, prev)=> console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true);
            props.setGlobalState({
              ignore: props.name,
              user: {
                name: props.name,
              },
            });
          }
          
          
          if (!window.__POWERED_BY_QIANKUN__) {
            render({});
          }
          
          /**
           * bootstrap 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子,不會(huì)再重復(fù)觸發(fā) bootstrap。
           * 通常我們可以在這里做一些全局變量的初始化,比如不會(huì)在 unmount 階段被銷毀的應(yīng)用級(jí)別的緩存等。
           */
          export async function bootstrap() {
            console.log('[react16] react app bootstraped');
          }
          
          /**
           * 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法
           */
          export async function mount(props) {
            console.log('[react16] props from main framework', props);
            storeTest(props);
            render(props);
          }
          
          /**
           * 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例
           */
          export async function unmount(props) {
            const { container }=props;
            ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
          }
          

          3. 配置打包工具:

          為了讓主應(yīng)用能正確識(shí)別微應(yīng)用暴露出來的一些全局信息和開發(fā)環(huán)境下的跨域兼容,在子應(yīng)用(以create-react-app出來的react項(xiàng)目為例)安裝@rescripts/cli,并在子應(yīng)用目錄下新建.rescriptsrc.js,內(nèi)容如下:

          const { name }=require('./package');
          
          module.exports={
            webpack: (config)=> {
              config.output.library=`${name}-[name]`;
              config.output.libraryTarget='umd'; // 為了能通過window['app-name1']拿到子應(yīng)用聲明的生命周期
              // webpack 5 需要把 jsonpFunction 替換成 chunkLoadingGlobal
              config.output.jsonpFunction=`webpackJsonp_${name}`; 
              config.output.globalObject='window';
          
              return config;
            },
          
            devServer: (_)=> {
              const config=_;
          
              config.headers={
                'Access-Control-Allow-Origin': '*',
              };
              config.historyApiFallback=true;
              config.hot=false;
              config.watchContentBase=false;
              config.liveReload=false;
          
              return config;
            },
          };
          

          4. 預(yù)覽

          使用strictStyleIsolation:true方式進(jìn)行樣式隔離,會(huì)生成一個(gè)shadow dom,進(jìn)行樣式的完全隔離:

          使用experimentalStyleIsolation:true的方式進(jìn)行樣式隔離,會(huì)在css選擇器前添加特殊標(biāo)識(shí):

          可以看到,qiankun會(huì)將子應(yīng)用的html渲染到自定義的container中。 主應(yīng)用加載的是子應(yīng)用的html,在解析子應(yīng)用的html的過程中遇到j(luò)s和css會(huì)載框架內(nèi)進(jìn)行沙盒處理,完成css和js的隔離,之后下載并執(zhí)行,完成整個(gè)子應(yīng)用的渲染過程。

          框架優(yōu)缺點(diǎn)

          優(yōu)點(diǎn)

          • html entry的接入方式,不需要自己寫load方法,而是直接寫子應(yīng)用的訪問鏈接就可以。
          • 提供js沙箱
          • 提供樣式隔離,兩種方式可選
          • 資源預(yù)加載,在瀏覽器空閑時(shí)間預(yù)加載未打開的微應(yīng)用資源,加速微應(yīng)用打開速度。
          • 社區(qū)活躍
          • umi 插件,提供了 @umijs/plugin-qiankun 供 umi 應(yīng)用一鍵切換成微前端架構(gòu)系統(tǒng) 除了最后一點(diǎn)拓展以外,微前端想要達(dá)到的效果都已經(jīng)達(dá)到。
          • 應(yīng)用間通信簡(jiǎn)單,全局注入
          • 路由保持,瀏覽器刷新、前進(jìn)、后退,都可以作用到子應(yīng)用

          缺點(diǎn)

          • 改造成本較大,從 webpack、代碼、路由等等都要做一系列的適配
          • 對(duì) eval 的爭(zhēng)議,eval函數(shù)的安全和性能是有一些爭(zhēng)議的:MDN的eval介紹;
          • 無法同時(shí)激活多個(gè)子應(yīng)用,也不支持子應(yīng)用保活
          • 無法支持 vite 等 ESM 腳本運(yùn)行

          wujie

          目前(2024年4月)github star 3.7k
          wujie是騰訊出品。基于
          webcomponent 容器 + iframe 沙箱,能夠完善的解決適配成本、樣式隔離、運(yùn)行性能、頁面白屏、子應(yīng)用通信、子應(yīng)用保活、多應(yīng)用激活、vite 框架支持、應(yīng)用共享等

          實(shí)現(xiàn)方案

          • 無界利用iframe和webcomponent來搭建天然的js隔離沙箱css隔離沙箱,
          • 利用iframe的history主應(yīng)用的history在同一個(gè)top-level browsing context來搭建天然的路由同步機(jī)制
          • 支持以fiber的形式執(zhí)行js,由于子應(yīng)用的執(zhí)行會(huì)阻塞主應(yīng)用的渲染線程,當(dāng)fiber設(shè)置為true,那么js執(zhí)行時(shí)采取類似react fiber的模式方式間斷執(zhí)行,每個(gè) js 文件的執(zhí)行都包裹在requestidlecallback中,每執(zhí)行一個(gè)js可以返回響應(yīng)外部的輸入,但是這個(gè)顆粒度是js文件,如果子應(yīng)用單個(gè)js文件過大,可以通過拆包的方式降低達(dá)到fiber模式效益最大化

          wujie是如何渲染子應(yīng)用的?

          wujie跟qiankun一樣,都是基于html entry加載的,但他們解析html的過程是不一樣的。 qiankun是直接解析并執(zhí)行js、css、html的,而wujie則是先解析html,提取出script腳本放入空的iframe中,提取出css、html放入到web components中,具體來說:

          1. 解析入口 HTML ,分別得到script、css、模版html
          2. 創(chuàng)建一個(gè)純凈的 iframe,為了實(shí)現(xiàn)應(yīng)用間(iframe 間)通訊,無界子應(yīng)用 iframe 的 url 會(huì)設(shè)置為主應(yīng)用的域名(同域),因此 iframe 的 location.href 并不是子應(yīng)用的 url。創(chuàng)建好后停止加載iframe。
          3. iframe內(nèi)插入js,將抽離出來的script腳本,插到iframe中去,在iframe中執(zhí)行子應(yīng)用的js
          4. 創(chuàng)建web component,id為子應(yīng)用id,將抽離出來的html插入。
          5. 由于iframe內(nèi)的js有可能操作dom,但是iframe內(nèi)沒有dom,隨意wujie框架內(nèi)對(duì)iframe攔截document對(duì)象,統(tǒng)一將dom指向shadowRoot,此時(shí)比如新建元素、彈窗或者冒泡組件就可以正常約束在shadowRoot內(nèi)部。

          wujie實(shí)戰(zhàn)

          wujie接入很簡(jiǎn)單,主應(yīng)用可以讓開發(fā)者以組件的方式加載子應(yīng)用。子應(yīng)用只需要做支持跨域請(qǐng)求改造,這個(gè)是所有微前端框架運(yùn)行的前提,除此之外子應(yīng)用可以不做任何改造就可以在無界框架中運(yùn)行,不過此時(shí)運(yùn)行的方式是重建模式。 子應(yīng)用也可以配置保活、生命周期適配進(jìn)入保活模式或單例模式。

          1. 主應(yīng)用入口文件

          與其他框架一樣,先配置子應(yīng)用,

          // main-react/index.js
          import "react-app-polyfill/stable";
          import "react-app-polyfill/ie11";
          
          import React from "react";
          import ReactDOM from "react-dom";
          import WujieReact from "wujie-react";
          import "./index.css";
          import App from "./App";
          import hostMap from "./hostMap";
          import credentialsFetch from "./fetch";
          import lifecycles from "./lifecycle";
          import plugins from "./plugin";
          
          const { setupApp, preloadApp, bus }=WujieReact;
          const isProduction=process.env.NODE_ENV==="production";
          bus.$on("click", (msg)=> window.alert(msg));
          
          const degrade=window.localStorage.getItem("degrade")==="true" || !window.Proxy || !window.CustomElementRegistry;
          /**
           * 大部分業(yè)務(wù)無需設(shè)置 attrs
           * 此處修正 iframe 的 src,是防止github pages csp報(bào)錯(cuò)
           * 因?yàn)槟J(rèn)是只有 host+port,沒有攜帶路徑
           */
          const attrs=isProduction ? { src: hostMap("//localhost:7700/") } : {};
          /**
           * 配置應(yīng)用,主要是設(shè)置默認(rèn)配置
           * preloadApp、startApp的配置會(huì)基于這個(gè)配置做覆蓋
           */
          setupApp({
            name: "react16",
            url: hostMap("//localhost:7600/"),
            attrs, // 子應(yīng)用iframe的src
            exec: true, // 預(yù)執(zhí)行
            fetch: credentialsFetch, // 自定義的fetch方法
            plugins,
            /** 子應(yīng)用短路徑替換,路由同步時(shí)生效 */
            prefix: { "prefix-dialog": "/dialog", "prefix-location": "/location" },
             /** 子應(yīng)用采用降級(jí)iframe方案 */
            degrade,
            ...lifecycles,
          });
          
          setupApp({
            name: "vue3",
            url: hostMap("//localhost:7300/"),
            attrs,
            exec: true,
            alive: true, // 子應(yīng)用保活,state不會(huì)丟失
            plugins: [{ cssExcludes: ["https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"] }],
            // 引入了的第三方樣式不需要添加credentials
            fetch: (url, options)=>
              url.includes(hostMap("//localhost:7300/")) ? credentialsFetch(url, options) : window.fetch(url, options),
            degrade,
            ...lifecycles,
          });
          
          
          
          if (window.localStorage.getItem("preload") !=="false") {
            preloadApp({
              name: "react16",
            });
           
            if (window.Proxy) {
              preloadApp({
                name: "vue3",
              });
            }
          }
          
          ReactDOM.render(<App />, document.getElementById("root"));
          
          

          引入子應(yīng)用的地方直接以組件式的方式引入:

          import React from "react";
          import hostMap from "../hostMap";
          import WujieReact from "wujie-react";
          import { useNavigate, useLocation } from "react-router-dom";
          
          export default function React16() {
            const navigation=useNavigate();
            const location=useLocation();
            const path=location.pathname.replace("/react16-sub", "").replace("/react16", "").replace("/",""); ////
            const react16Url=hostMap("//localhost:7600/") + path;
            const props={
              jump: (name)=> {
                navigation(`/${name}`);
              },
            };
            return (
              // 單例模式,name相同則復(fù)用一個(gè)無界實(shí)例,改變url則子應(yīng)用重新渲染實(shí)例到對(duì)應(yīng)路由
              <WujieReact
                width="100%"
                height="100%"
                name="react16"
                url={react16Url}
                sync={!path}
                props={props}
              ></WujieReact>
            );
          }
          
          

          2. 預(yù)覽

          框架優(yōu)缺點(diǎn)

          優(yōu)點(diǎn)

          • 接入簡(jiǎn)單,可以以組件的方式引入子應(yīng)用
          • 純凈無污染
            • 無界利用iframe和webcomponent來搭建天然的js隔離沙箱和css隔離沙箱
            • 利用iframe的history和主應(yīng)用的history在同一個(gè)top-level browsing context來搭建天然的路由同步機(jī)制
            • 副作用局限在沙箱內(nèi)部,子應(yīng)用切換無需任何清理工作,沒有額外的切換成本
          • 支持vite esmoudle加載,由于js是獨(dú)立在iframe中加載的,所以支持esmodule加載
          • 支持預(yù)加載
          • 支持應(yīng)用保活,子應(yīng)用狀態(tài)保留,由于是獨(dú)立在iframe中的,而切換應(yīng)用時(shí)不會(huì)移除iframe,所以子應(yīng)用的狀態(tài)會(huì)被保留在原來的iframe中,當(dāng)主應(yīng)用再次渲染子應(yīng)用dom時(shí),會(huì)顯示之前的狀態(tài)。
          • 多應(yīng)用同時(shí)激活在線

          缺點(diǎn)

          1. iframe沙箱的src設(shè)置了主應(yīng)用的host,初始化iframe的時(shí)候需要等待iframe的location.orign從'about:blank'初始化為主應(yīng)用的host,這個(gè)采用的計(jì)時(shí)器去等待的不是很優(yōu)雅。

          Micro App

          截至目前(2024年4月)github star 5.2k
          mirco-app 是京東2021年開源的一款微前端框架。它借助了瀏覽器對(duì) webComponent 的支持,實(shí)現(xiàn)了一套微前端方案體系。并且由于 Shadow Dom 對(duì) react 這類庫的兼容性較差,便自己實(shí)現(xiàn)了類 Shadow Dom 的效果。與 qiankun 相比,接入更加簡(jiǎn)單。最新的版本也支持iframe實(shí)現(xiàn)js隔離,類似wujie

          實(shí)現(xiàn)方案

          首先micro-app實(shí)現(xiàn)了一個(gè)基于WebComponent的組件,并實(shí)現(xiàn)了類Shadow Dom 的效果,開發(fā)者只需要用<micro-app name="xx" url="xx" baseroute="/xxx/xxx">來加載子應(yīng)用,整個(gè)對(duì)子應(yīng)用的加載、js隔離、css隔離的邏輯都封裝在了web component組件<micro-app>中,具體來說:

          1. 當(dāng)調(diào)用microApp.start()后,會(huì)注冊(cè)一個(gè)名為micro-app 的自定義 webComponent 標(biāo)簽。我們可以從 <micro-app name='app1' url='xx' baseroute='/my-page'></micro-app> 中拿到子應(yīng)用的線上入口地址。
          2. <micro-app>組件內(nèi)部,當(dāng)匹配到路由后,跟qiankun一樣加載html,得到html字符串模版
          3. 分析html字符串,提取<head>頭和<body>,并替換為框架自定義標(biāo)簽<micro-app-head>和<micro-app-body>
          4. 在<micro-app-head>內(nèi),會(huì)對(duì)script標(biāo)簽和link標(biāo)簽的內(nèi)容進(jìn)行加載并執(zhí)行
          5. 將<micro-app-head>和<micro-app-body>插入到<micro-app>標(biāo)簽內(nèi)
          6. <micro-app>內(nèi)提供了js沙箱方法(v1.0以前跟qiankun沙箱一樣),<micro-app-head>掛載到<micro-app>后,內(nèi)部會(huì)逐一對(duì)<micro-app-head>內(nèi)的script標(biāo)簽的js綁定作用域,實(shí)現(xiàn)js隔離。

          css隔離方案

          默認(rèn)使用正則將CSS字符串切割成最小單元,每個(gè)單元包含一段CSS信息,將所有的信息整理生成CSSTree,遍歷CSSTree的每個(gè)規(guī)則,添加前綴實(shí)現(xiàn)樣式隔離。

          js隔離方案

          micro-app有兩種方式實(shí)現(xiàn)js隔離,默認(rèn)是跟qiankun一樣采用proxy沙箱的方式隔離, 在v1.0發(fā)布后支持了基于原生iframe的隔離方式。

          Micro App實(shí)戰(zhàn)

          1. 主應(yīng)用入口文件

          import React from 'react';
          import ReactDOM from 'react-dom';
          import './index.css';
          import Router from './router';
          import microApp from '@micro-zoe/micro-app'
          
          microApp.start()
          
          ReactDOM.render(
            <React.StrictMode>
              <Router />
            </React.StrictMode>,
            document.getElementById('root')
          );
          
          

          調(diào)用子應(yīng)用

          export function MyPage () {
            return (
              <div>
                <h1>子應(yīng)用</h1>
                // name:應(yīng)用名稱, url:應(yīng)用地址
                <micro-app name='my-app' url='http://localhost:3000/'></micro-app>
              </div>
            )
          }
          
          

          2.預(yù)覽

          框架優(yōu)缺點(diǎn)

          優(yōu)點(diǎn)

          • 接入簡(jiǎn)單,組件式引入子應(yīng)用
          • 團(tuán)隊(duì)持續(xù)更新維護(hù)
          • js隔離、css隔離、路由同步
          • 支持子應(yīng)用保活, 需要開啟keep-alive模式
          • 支持fiber模式,提升主應(yīng)用的渲染性能。

          缺點(diǎn)

          • 1.0之前不支持vite,1.0之后支持了
          • 默認(rèn)css隔離方式,主應(yīng)用的樣式還是會(huì)污染到子應(yīng)用。
          • 子應(yīng)用和主應(yīng)用必須相同的路由模式,要么同時(shí)hash模式,要么同時(shí)history模式
          • 依賴于CustomElements和Proxy兩個(gè)較新的API。Proxy暫時(shí)沒有做兼容,所以對(duì)于不支持Proxy的瀏覽器無法運(yùn)行micro-app。

          作者:韓國芳

          來源:微信公眾號(hào):奇舞精選

          出處:https://mp.weixin.qq.com/s/997pVVxdgpOH6ZsDsFAh2g

          并讠果:quangneng.com/5131/

          Swift 是蘋果公司于2014年推出的一種新的編程語言,用于iOS、macOS、watchOS和tvOS應(yīng)用程序的開發(fā)。Swift因其安全性、速度和現(xiàn)代性而受到開發(fā)者的喜愛。在這篇文章中,我們將從入門到進(jìn)階,通過實(shí)戰(zhàn)探探iOS APP,來學(xué)習(xí)Swift編程。

          一、Swift入門

          1. 環(huán)境搭建

          首先,我們需要在蘋果官方網(wǎng)站下載并安裝Xcode,Xcode是蘋果公司提供的IDE,用于Swift語言的開發(fā)。安裝完成后,打開Xcode,創(chuàng)建一個(gè)新的Swift項(xiàng)目,即可開始Swift編程。

          1. 基礎(chǔ)語法

          Swift的基礎(chǔ)語法包括變量、數(shù)據(jù)類型、運(yùn)算符、控制結(jié)構(gòu)等。熟悉這些基礎(chǔ)知識(shí)是進(jìn)行Swift編程的前提。

          1. 函數(shù)和閉包

          Swift支持函數(shù)和閉包的概念。函數(shù)是一段具有特定功能的代碼,而閉包則是一種匿名函數(shù)。掌握函數(shù)和閉包的定義和使用,有助于我們編寫結(jié)構(gòu)清晰、易于維護(hù)的代碼。

          1. 類和結(jié)構(gòu)體

          Swift通過類和結(jié)構(gòu)體實(shí)現(xiàn)面向?qū)ο缶幊獭n惡徒Y(jié)構(gòu)體都是用于封裝數(shù)據(jù)和行為的數(shù)據(jù)類型,但類支持繼承和多態(tài),而結(jié)構(gòu)體則不支持。了解它們的使用場(chǎng)景和操作方法,可以更好地設(shè)計(jì)程序。

          1. 枚舉和協(xié)議

          枚舉和協(xié)議是Swift語言中常用的特性。枚舉用于定義一組具有共同屬性和行為的值,協(xié)議則用于定義一組方法或?qū)傩浴U莆彰杜e和協(xié)議,可以更好地組織代碼,提高可維護(hù)性。

          二、Swift進(jìn)階

          1. 錯(cuò)誤處理

          在Swift中,錯(cuò)誤處理是一個(gè)重要的環(huán)節(jié)。通過Error協(xié)議和do-catch語句,我們可以有效地處理程序運(yùn)行過程中出現(xiàn)的錯(cuò)誤。

          1. 擴(kuò)展和泛型

          Swift支持?jǐn)U展和泛型編程。擴(kuò)展用于為現(xiàn)有的類、結(jié)構(gòu)體、枚舉和協(xié)議添加新的功能,泛型則用于編寫可復(fù)用的、類型安全的代碼。

          1. 多線程編程

          Swift支持多線程編程,通過DispatchQueue和OperationQueue等API實(shí)現(xiàn)。掌握多線程編程,可以充分利用計(jì)算機(jī)資源,提高程序性能。

          三、實(shí)戰(zhàn)探探iOS APP

          1. 項(xiàng)目背景

          探探是一款流行的社交應(yīng)用,用戶可以通過滑動(dòng)來選擇喜歡或不喜歡的人。在這個(gè)項(xiàng)目中,我們將使用Swift語言開發(fā)一個(gè)簡(jiǎn)化版的探探應(yīng)用。

          1. 功能需求

          我們的簡(jiǎn)化版探探應(yīng)用將具備以下功能:

          (1)用戶注冊(cè)和登錄;

          (2)用戶可以上傳頭像和填寫個(gè)人信息;

          (3)用戶可以查看其他用戶的資料,并進(jìn)行喜歡或不喜歡操作;

          (4)用戶可以查看喜歡自己的人,并進(jìn)行配對(duì)操作。

          1. 技術(shù)實(shí)現(xiàn)

          在這個(gè)項(xiàng)目中,我們將使用以下技術(shù):

          (1)使用UIKit構(gòu)建用戶界面;

          (2)使用Core Data進(jìn)行數(shù)據(jù)存儲(chǔ);

          (3)使用 Alamofire 進(jìn)行網(wǎng)絡(luò)請(qǐng)求;

          (4)使用 SwiftSoup 解析 HTML 數(shù)據(jù)。

          1. 開發(fā)實(shí)踐

          在開發(fā)過程中,我們遵循Swift編程規(guī)范,采用面向?qū)ο缶幊獭㈠e(cuò)誤處理、多線程編程等技術(shù),保證代碼質(zhì)量。同時(shí),我們編寫了詳細(xì)的單元測(cè)試和文檔,確保應(yīng)用的穩(wěn)定性和可維護(hù)性。

          1. 性能優(yōu)化

          為了保證應(yīng)用的高性能,我們采用了以下優(yōu)化措施:

          (1)使用懶加載和緩存技術(shù),減少不必要的資源消耗;

          (2)使用UITableView進(jìn)行列表展示,提高列表的滑動(dòng)性能;

          (3)使用GCD進(jìn)行圖片異步加載,提高圖片加載速度。

          1. 項(xiàng)目部署

          在項(xiàng)目部署階段,我們使用Xcode進(jìn)行應(yīng)用的打包和發(fā)布。同時(shí),我們使用TestFlight進(jìn)行應(yīng)用的測(cè)試和分發(fā)。

          四、Swift高級(jí)特性

          1. 內(nèi)存管理

          Swift使用自動(dòng)引用計(jì)數(shù)(ARC)機(jī)制來管理內(nèi)存,但開發(fā)者仍需理解內(nèi)存管理的概念,以避免內(nèi)存泄漏和野指針問題。了解Swift中的強(qiáng)引用、弱引用、無主引用等概念對(duì)于編寫高效和安全的代碼至關(guān)重要。

          1. 協(xié)議擴(kuò)展

          Swift的協(xié)議擴(kuò)展允許開發(fā)者在不修改原有類的情況下,為協(xié)議提供默認(rèn)實(shí)現(xiàn)。這一特性極大地增強(qiáng)了代碼的復(fù)用性和靈活性。

          1. 泛型編程

          泛型是Swift中的一個(gè)強(qiáng)大特性,它允許開發(fā)者編寫適用于任何類型的代碼。泛型函數(shù)和泛型類型能夠提高代碼的通用性和類型安全。

          1. 動(dòng)態(tài)性

          Swift雖然是一門靜態(tài)語言,但它也提供了一定程度的動(dòng)態(tài)性。例如,使用dynamic關(guān)鍵字可以使類成員動(dòng)態(tài)派發(fā),從而支持運(yùn)行時(shí)檢查和修改。

          五、iOS設(shè)計(jì)模式與實(shí)踐

          1. MVC模式

          Model-View-Controller(MVC)是iOS開發(fā)中最常用的設(shè)計(jì)模式。了解MVC模式的原則,能夠幫助開發(fā)者更好地組織代碼,實(shí)現(xiàn)視圖、模型和控制器之間的解耦。

          1. MVVM模式

          Model-View-ViewModel(MVVM)是MVC模式的一種變體,它通過引入ViewModel層來進(jìn)一步解耦視圖和模型,提高代碼的可測(cè)試性和可維護(hù)性。

          1. 單例模式

          單例模式確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)全局訪問點(diǎn)。在iOS開發(fā)中,單例模式常用于管理共享資源或提供全局服務(wù)。

          1. 依賴注入

          依賴注入是一種設(shè)計(jì)模式,用于實(shí)現(xiàn)控制反轉(zhuǎn)。在Swift中,依賴注入可以幫助我們減少組件之間的耦合,提高代碼的可測(cè)試性和可維護(hù)性。

          六、iOS應(yīng)用測(cè)試與優(yōu)化

          1. 單元測(cè)試

          單元測(cè)試是確保代碼質(zhì)量的關(guān)鍵。在Swift中,使用XCTest框架進(jìn)行單元測(cè)試,可以驗(yàn)證代碼的各個(gè)部分是否按預(yù)期工作。

          1. UI測(cè)試

          UI測(cè)試用于驗(yàn)證應(yīng)用的界面和用戶交互是否符合預(yù)期。使用XCTest框架進(jìn)行UI測(cè)試,可以確保應(yīng)用的用戶體驗(yàn)質(zhì)量。

          1. 性能優(yōu)化

          性能優(yōu)化是iOS應(yīng)用開發(fā)的重要環(huán)節(jié)。使用Instruments等工具進(jìn)行性能分析,可以幫助開發(fā)者發(fā)現(xiàn)并解決性能瓶頸。

          1. 代碼審查

          代碼審查是提高代碼質(zhì)量的有效手段。通過代碼審查,可以及時(shí)發(fā)現(xiàn)和修復(fù)代碼中的問題,提高代碼的可讀性和可維護(hù)性。

          七、項(xiàng)目總結(jié)與展望

          通過實(shí)戰(zhàn)探探iOS APP的開發(fā),我們不僅學(xué)習(xí)了Swift編程語言的基礎(chǔ)和進(jìn)階知識(shí),還了解了iOS應(yīng)用開發(fā)的最佳實(shí)踐和設(shè)計(jì)模式。項(xiàng)目實(shí)踐中,我們遇到了各種挑戰(zhàn),如網(wǎng)絡(luò)請(qǐng)求的異步處理、用戶界面的流暢度優(yōu)化、數(shù)據(jù)存儲(chǔ)的可靠性等,通過不斷學(xué)習(xí)和嘗試,我們逐一解決了這些問題,最終完成了一個(gè)功能完善、性能良好的社交應(yīng)用。

          展望未來,隨著Swift語言的不斷發(fā)展和iOS平臺(tái)的新技術(shù)涌現(xiàn),iOS應(yīng)用開發(fā)將變得更加高效和強(qiáng)大。作為開發(fā)者,我們需要不斷學(xué)習(xí)新技術(shù),提高自己的技能水平,以適應(yīng)不斷變化的技術(shù)環(huán)境。Swift和iOS開發(fā)將繼續(xù)為我們提供廣闊的發(fā)展空間和無限的創(chuàng)新可能。


          、CSS初認(rèn)識(shí)


          1、趣調(diào)查


          2、“裝修小能手”

          前面我們研究了HTML,回顧下它是做什么的?

          當(dāng)我們用HTML搭建好網(wǎng)頁的基本骨架,下面請(qǐng)出我們的“裝修小能手”--CSS。

          3、如何學(xué)習(xí)CSS?

          Python大星前去探探路...

          4、學(xué)習(xí)必備

          ● 充分利用谷歌瀏覽器Chrome審查元素功能

          CSS權(quán)威網(wǎng)站

          https://developer.mozilla.org/zh-CN/docs/Web/CSS/Reference


          二、CSS的基本使用

          1、基本問題

          ● CSS代碼寫在什么地方?

          ● CSS的語法規(guī)則?

          2、CSS代碼的書寫位置

          ● 內(nèi)部樣式表

          書寫在style元素中,一般放在<head></head>中。

          有人可能會(huì)問,能放到其他元素里嗎?

          答案:可以。但如果你使用內(nèi)部樣式表,建議放到head元素中,利于瀏覽器的加載渲染

          >> 舉個(gè)栗子:


          ● 內(nèi)聯(lián)樣式表(元素樣式表)

          直接書寫在元素的全局屬性style中


          ● 外部樣式表

          將樣式書寫到獨(dú)立的css文件中。

          【1】理由有三:

          ① 解決多頁面樣式重復(fù)的問題;

          ② 有利于瀏覽器緩存,提高頁面響應(yīng)速度;

          ③ 有利于代碼分離,易閱讀和維護(hù)。

          【2】如何使用外部樣式表:


          3、CSS代碼的語法

          CSS語法=選擇器 + 聲明塊

          ● 選擇器(Selector

          CSS 選擇器是CSS規(guī)則的一部分,使你能應(yīng)用樣式到指定元素。

          ① 基礎(chǔ)選擇器

          • 標(biāo)簽選擇器 elementname

          • 類選擇器 .classname

          • ID 選擇器 #idname

          • 屬性選擇器 [attr=value]

          ① 關(guān)系選擇器

          • 鄰近兄弟元素選擇器 A + B

          選擇緊跟A元素后的B元素,用+表示,選擇相鄰的第一個(gè)兄弟元素。


          • 兄弟元素選擇器 A ~ B

          選擇A元素之后的所有兄弟元素B,作用于多個(gè)元素,用~隔開

          • 直接子元素選擇器 A > B

          選擇所有作為A元素的直接子元素B,對(duì)更深一層的元素不起作用,用>表示

          • 后代元素選擇器 A B

          選擇所有被A元素包含的B元素,中間用空格隔開,在CSS使用頻率高


          ③ 偽類選擇器

          選中某些元素的某種狀態(tài)

          1)link: 超鏈接未訪問時(shí)的狀態(tài)
          2)visited: 超鏈接訪問過后的狀態(tài)
          3)hover: 鼠標(biāo)懸停狀態(tài)
          4)active:激活狀態(tài),鼠標(biāo)按下狀態(tài)
          愛恨法則:love hate
          

          ● 聲明塊

          出現(xiàn)在大括號(hào){}中

          聲明塊中包含很多聲明(屬性),每一個(gè)聲明(屬性)表達(dá)了某一方面的樣式。


          主站蜘蛛池模板: 日本成人一区二区| 无码av中文一区二区三区桃花岛 | 午夜视频久久久久一区| 农村人乱弄一区二区| 夜夜爽一区二区三区精品| 无码午夜人妻一区二区不卡视频 | 深夜福利一区二区| 成人在线观看一区| 亚洲AV成人一区二区三区观看| 骚片AV蜜桃精品一区| 精品国产免费一区二区三区香蕉| www.亚洲一区| 国产日韩AV免费无码一区二区三区| 国产av一区最新精品| 色婷婷亚洲一区二区三区| 亚洲av成人一区二区三区在线播放 | 精品三级AV无码一区| 性无码免费一区二区三区在线| 夜夜添无码一区二区三区| 激情内射亚州一区二区三区爱妻| 国产成人无码精品一区在线观看| 一区二区三区视频免费| 一区二区三区四区在线视频| 国产福利电影一区二区三区| 国产成人高清精品一区二区三区| 丝袜人妻一区二区三区网站| 好爽毛片一区二区三区四| 国产成人无码一区二区在线观看| 日韩精品无码视频一区二区蜜桃| 美女啪啪一区二区三区| 一区二区在线视频| 亚洲av色香蕉一区二区三区| 日本国产一区二区三区在线观看| 国模大胆一区二区三区| 国产91一区二区在线播放不卡| 狠狠色婷婷久久一区二区 | 国产在线观看精品一区二区三区91| 视频精品一区二区三区| 精品国产一区二区22| 日韩内射美女人妻一区二区三区| 伊人色综合视频一区二区三区|