国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

徹底理清前端單頁面應(yīng)用(SPA)的實(shí)現(xiàn)原理 【精讀源碼】

sunny5541 / 2272人閱讀

隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢?
優(yōu)質(zhì)gitHub開源練手項(xiàng)目:

React移動端從零開始手寫一個(gè)優(yōu)化版腳手架

Electron跨平臺應(yīng)用DEMO

手寫的Node.js靜態(tài)資源服務(wù)器

React 同構(gòu)ssr

Node.js爬蟲輸出PDF文件

先說說原始的MPA多頁面應(yīng)用:
文末還有新建的QQ以及微信群哦~ 歡迎大家加入~~
傳統(tǒng)的多頁面應(yīng)用構(gòu)建方式:

純服務(wù)端渲染,前后端不分離,使用jsp,jade,"ejs","tempalte"等技術(shù)在后臺先拼接成對應(yīng)的HTML結(jié)構(gòu),然后轉(zhuǎn)換成字符串,在每個(gè)對應(yīng)的路由返回對應(yīng)的數(shù)據(jù)(文件)即可

Jade模版服務(wù)端渲染,代碼實(shí)現(xiàn):
const express= require("express")
const app =express()
const jade = require("jade")
const result = ***
const url path = *** 
const html = jade.renderFile(url, { data: result, urlPath })//傳入數(shù)據(jù)給模板引擎
app.get("/",(req,res)=>{
    res.send(html)//直接吐渲染好的`html`文件拼接成字符串返回給客戶端
}) //RestFul接口 

app.listen(3000,err=>{
    //do something
})

使用jQuery等傳統(tǒng)庫繪制的前端頁面

傳統(tǒng)前后端不分離,服務(wù)端渲染的優(yōu)缺點(diǎn): 優(yōu)點(diǎn):

SEO友好,因?yàn)榉祷亟o前端的是渲染好的HTML結(jié)構(gòu),里面的內(nèi)容都可以被爬蟲抓取到。

對于一些應(yīng)用性能等要求不高的項(xiàng)目,比如某個(gè)公司的靜態(tài)網(wǎng)頁,內(nèi)容很少的情況下,直接一把梭就好,不用再搭建工程化的環(huán)境等

對于后端程序員(全干工程師)來說,不用去特意學(xué)習(xí)前端框架,公司也不用特意去招聘前端

兼容性好,傳統(tǒng)服務(wù)端渲染多頁面應(yīng)用吐出來的都是字符串,HTML結(jié)構(gòu)

缺點(diǎn):

如果項(xiàng)目很大,不利于維護(hù),據(jù)我所知,目前很多云計(jì)算公司,還有不少都是使用非單頁面應(yīng)用,例如一個(gè)幾十萬行的項(xiàng)目是用jQuery寫的,如果注釋和文檔不是非常齊全,那么真的會無從下手

性能和用戶體驗(yàn),不能跟單頁面應(yīng)用相比

后期迭代,升級空間不大,目前大部分寫得比較好的庫,都建立vue,react等框架基礎(chǔ)上,他們都有一套自己的運(yùn)行機(jī)制,有自己的生命周期,并且不像傳統(tǒng)的應(yīng)用,還加上了一層虛擬DOM以及diff算法

現(xiàn)在類似Ant-Design-pro這樣的開箱即用的庫已經(jīng)很多,單頁面應(yīng)用的學(xué)習(xí)和開發(fā)成本已經(jīng)很低很低,如果還在使用傳統(tǒng)的技術(shù)去開發(fā)新的應(yīng)用,對于開發(fā)人員多內(nèi)心來說也是一種折磨。

這里并不是說多頁面應(yīng)用不好,只能說各有各自的好,單頁面應(yīng)用如果通過大量的極致優(yōu)化手段,是可以從不少方面跟原生一拼。

目前的單頁面應(yīng)用:

只有一張Web頁面的應(yīng)用,是一種從Web服務(wù)器加載的富客戶端,單頁面跳轉(zhuǎn)僅刷新局部資源 ,公共資源(js、css等)僅需加載一次,常用于PC端官網(wǎng)、購物等網(wǎng)站

其實(shí)只有一個(gè)空的DIV標(biāo)簽,其他都是js動態(tài)生態(tài)的內(nèi)容

單頁面應(yīng)用實(shí)現(xiàn)步驟: 代碼實(shí)現(xiàn):

首先是一個(gè)靜態(tài)模板文件 index.html





    
    
    
    Document



    

vue react框架的入口文件中指定對應(yīng)的渲染元素:

import React from "react;
import ReactDOM from "react-dom";

ReactDOM.render(
,
document.querySelector("#root")
)

引入react-router或者 react-router-dom,dva等路由跳轉(zhuǎn)的庫

配置路由跳轉(zhuǎn)

//這里使用HashRouter
      //React錯(cuò)誤邊界
        
          
          
          //404路由或者重定向都可以
        
      
單頁面應(yīng)用所謂路由跳轉(zhuǎn),其實(shí)最終結(jié)果就是:

瀏覽器的url地址發(fā)生變化,但是其實(shí)并沒有發(fā)送請求,也沒有刷新整個(gè)頁面

根據(jù)我們配置的路由信息,每次點(diǎn)擊切換路由,會切換到不同的組件顯示,類似于選項(xiàng)卡功能的實(shí)現(xiàn),但是同時(shí)url地址欄會變化

分為HashRouterBrowserRouter兩種模式

自己實(shí)現(xiàn)一個(gè)粗略的路由跳轉(zhuǎn): 自己實(shí)現(xiàn)傳統(tǒng)的Hash模式跳轉(zhuǎn):
hash 就是指 url 后的 # 號以及后面的字符。例如www.baidu.com/#segmentfault,那么#segmentfault就是hash

需要用到的幾個(gè)知識點(diǎn):

window.location.hash = "**"; // 設(shè)置當(dāng)前的hash值

const hash = window.location.hash 獲取當(dāng)前的hash值

hash改變會觸發(fā)windowhashchange事件

window.onhashchange=function(e){
    let newURL = e.newURL; // 改變后的新 url地址
    let oldURL = e.oldURL; // 改變前的舊 url地址
}
這里特別注意,hash改變并不會發(fā)送請求
開始實(shí)現(xiàn)Hash模式跳轉(zhuǎn): 使用類似發(fā)布訂閱模式的方式,使用ES6的class實(shí)現(xiàn):

初始訂閱,每個(gè)不同的hash值,對應(yīng)不同的函數(shù)調(diào)用處理。

class Router {
  constructor() {
    this.routes = {};
    this.currentUrl = "";
  }
  route(path, callback) {
    this.routes[path] = callback || function() {};
  }
  updateView() {
    this.currentUrl = location.hash.slice(1) || "/";
    this.routes[this.currentUrl] && this.routes[this.currentUrl]();
  }
  init() {
    window.addEventListener("load", this.updateView.bind(this), false);
    window.addEventListener("hashchange", this.updateView.bind(this), false);
  }
}

routes 用來存放不同路由對應(yīng)的回調(diào)函數(shù)

init 用來初始化路由,在 load 事件發(fā)生后刷新頁面,并且綁定 hashchange 事件,當(dāng) hash 值改變時(shí)觸發(fā)對應(yīng)回調(diào)函數(shù)

開始使用:


這樣一個(gè)簡單的hash模式路由就做好了,剩下的就是路由嵌套,以及錯(cuò)誤邊界的處理
History模式實(shí)現(xiàn):

History來自Html5的規(guī)范

History模式,url地址欄的改變并不會觸發(fā)任何事件

History模式,可以使用history.pushState,history.replaceState來控制url地址,history.pushState() 和 history.replaceState() 的區(qū)別在于:

history.pushState() 在保留現(xiàn)有歷史記錄的同時(shí),將 url 加入到歷史記錄中。

history.replaceState() 會將歷史記錄中的當(dāng)前頁面歷史替換為 url。

History模式下,刷新頁面會404,需要后端配合匹配一個(gè)任意路由,重定向到首頁,特別是加上Nginx反向代理服務(wù)器的時(shí)候

我們需要換個(gè)思路,我們可以羅列出所有可能觸發(fā) history 改變的情況,并且將這些方式一一進(jìn)行攔截,變相地監(jiān)聽 history 的改變。
對于一個(gè)應(yīng)用而言,url 的改變(不包括 hash 值得改變)只能由下面三種情況引起:

點(diǎn)擊瀏覽器的前進(jìn)或后退按鈕

點(diǎn)擊 a 標(biāo)簽

在 JS 代碼中觸發(fā) history.push(replace)State 函數(shù)

只要對上述三種情況進(jìn)行攔截,就可以變相監(jiān)聽到 history 的改變而做出調(diào)整。針對情況 1,HTML5 規(guī)范中有相應(yīng)的 onpopstate 事件,通過它可以監(jiān)聽到前進(jìn)或者后退按鈕的點(diǎn)擊,值得注意的是,調(diào)用 history.push(replace)State 并不會觸發(fā) onpopstate 事件。
開始實(shí)現(xiàn):
class Router {
  constructor() {
    this.routes = {};
    this.currentUrl = "";
  }
  route(path, callback) {
    this.routes[path] = callback || function() {};
  }
  updateView(url) {
    this.currentUrl = url;
    this.routes[this.currentUrl] && this.routes[this.currentUrl]();
  }
  bindLink() {
    const allLink = document.querySelectorAll("a[data-href]");
    for (let i = 0, len = allLink.length; i < len; i++) {
      const current = allLink[i];
      current.addEventListener(
        "click",
        e => {
          e.preventDefault();
          const url = current.getAttribute("data-href");
          history.pushState({}, null, url);
          this.updateView(url);
        },
        false
      );
    }
  }
  init() {
    this.bindLink();
    window.addEventListener("popstate", e => {
      this.updateView(window.location.pathname);
    });
    window.addEventListener("load", () => this.updateView("/"), false);
  }
}
Router 跟之前 Hash 路由很像,不同的地方在于:

init 初始化函數(shù),首先需要獲取所有特殊的鏈接標(biāo)簽,然后監(jiān)聽點(diǎn)擊事件,并阻止其默認(rèn)事件,觸發(fā) history.pushState 以及更新相應(yīng)的視圖。

另外綁定 popstate 事件,當(dāng)用戶點(diǎn)擊前進(jìn)或者后退的按鈕時(shí)候,能夠及時(shí)更新視圖,另外當(dāng)剛進(jìn)去頁面時(shí)也要觸發(fā)一次視圖更新。

實(shí)際使用:


跟之前的 html 基本一致,區(qū)別在于用 data-href 來表示要實(shí)現(xiàn)軟路由的鏈接標(biāo)簽。

當(dāng)然上面還有情況 3,就是你在 JS 直接觸發(fā) pushState 函數(shù),那么這時(shí)候你必須要調(diào)用視圖更新函數(shù),否則就是出現(xiàn)視圖內(nèi)容和 url 不一致的情況。

setTimeout(() => {
  history.pushState({}, null, "/about");
  router.updateView("/about");
}, 2000);
React-router-dom源碼: Router組件:
export class Route extends Component {
  componentWillMount() {
    window.addEventListener("hashchange", this.updateView, false);
  }
  componentWillUnmount() {
    window.removeEventListener("hashchange", this.updateView, false);
  }
  updateView = () => {
    this.forceUpdate();
  }
  render() {
    const { path, exact, component } = this.props;
    const match = matchPath(window.location.hash, { exact, path });
    if (!match) {
      return null;
    }
    if (component) {
      return React.createElement(component, { match });
    }
    return null;
  }
}

組件掛載監(jiān)聽hash change原生事件,將要卸載時(shí)候移除事件監(jiān)聽防止內(nèi)存泄漏

每次hash改變,就觸發(fā)所有對應(yīng)hash的回掉,所有的Router都去更新視圖

每個(gè)Router組件中,都去對比當(dāng)前的hash值和這個(gè)組件的path屬性,如果不一樣,那么就返回null,·否則就渲染這個(gè)組件對應(yīng)的視圖

History模式的實(shí)現(xiàn):

實(shí)現(xiàn)History

這里想多留些時(shí)間寫其他源碼,這篇文章寫得非常好,大家也可以去看看,本文很多借鑒他的。
withRouter高階函數(shù)的源碼:
var withRouter = function withRouter(Component) {
  var C = function C(props) {
    var wrappedComponentRef = props.wrappedComponentRef,
        remainingProps = _objectWithoutProperties(props, ["wrappedComponentRef"]);

    return _react2.default.createElement(_Route2.default, {
      children: function children(routeComponentProps) {
        return _react2.default.createElement(Component, _extends({}, remainingProps, routeComponentProps, {
          ref: wrappedComponentRef
        }));
      }
    });
  };

  C.displayName = "withRouter(" + (Component.displayName || Component.name) + ")";
  C.WrappedComponent = Component;
  C.propTypes = {
    wrappedComponentRef: _propTypes2.default.func
  };

  return (0, _hoistNonReactStatics2.default)(C, Component);
};

傳入一個(gè)組件,返回一個(gè)新的組件,并且給這個(gè)組件賦予全局屬性,擁有路由組件的三大屬性

Switch組件:
Switch.prototype.render = function render() {
    var route = this.context.router.route;
    var children = this.props.children;

    var location = this.props.location || route.location;

    var match = void 0,
        child = void 0;
    _react2.default.Children.forEach(children, function (element) {
      if (match == null && _react2.default.isValidElement(element)) {
        var _element$props = element.props,
            pathProp = _element$props.path,
            exact = _element$props.exact,
            strict = _element$props.strict,
            sensitive = _element$props.sensitive,
            from = _element$props.from;

        var path = pathProp || from;

        child = element;
        match = (0, _matchPath2.default)(location.pathname, { path: path, exact: exact, strict: strict, sensitive: sensitive }, route.match);
      }
    });

    return match ? _react2.default.cloneElement(child, { location: location, computedMatch: match }) : null;
  };

遍歷所以傳入的子元素

如果有符合的路由對應(yīng)的元素,那么就返回,而且只匹配這一個(gè)路由。不再繼續(xù)往下匹配

如果第二條沒有找到符合的元素,那么拋出錯(cuò)誤

如果覺得寫得好,記得點(diǎn)個(gè)贊哦,另外新建了微信和QQ群,歡迎各位小哥哥小姐姐入駐~

微信群:

QQ群

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/106267.html

相關(guān)文章

  • 徹底理清前端頁面應(yīng)用SPA實(shí)現(xiàn)原理精讀源碼

    showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢? 優(yōu)質(zhì)gitHub開源練手項(xiàng)目: ...

    xiaodao 評論0 收藏0
  • 徹底理清前端頁面應(yīng)用SPA實(shí)現(xiàn)原理精讀源碼

    showImg(https://segmentfault.com/img/bVbvOmp?w=1612&h=888); 隨著React Vue前端框架的興起,出現(xiàn)了Vue-router,react-router-dom等前端路由管理庫,利用他們構(gòu)建出來的單頁面應(yīng)用,也是越來越接近原生的體驗(yàn),再也不是以前的點(diǎn)擊標(biāo)簽跳轉(zhuǎn)頁面,刷新整個(gè)頁面了,那么他們的原理是什么呢? 優(yōu)質(zhì)gitHub開源練手項(xiàng)目: ...

    崔曉明 評論0 收藏0
  • 前端基礎(chǔ)

    摘要:談起閉包,它可是兩個(gè)核心技術(shù)之一異步基于打造前端持續(xù)集成開發(fā)環(huán)境本文將以一個(gè)標(biāo)準(zhǔn)的項(xiàng)目為例,完全拋棄傳統(tǒng)的前端項(xiàng)目開發(fā)部署方式,基于容器技術(shù)打造一個(gè)精簡的前端持續(xù)集成的開發(fā)環(huán)境。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日...

    graf 評論0 收藏0
  • 深入了解頁面spa應(yīng)用(一)

    摘要:一,什么是單頁面應(yīng)用通俗的來講,就是一個(gè)應(yīng)用只有一個(gè)頁面,用戶通過切換路由和動態(tài)獲取數(shù)據(jù)達(dá)到頁面更新的目的,整個(gè)應(yīng)用的使用過程中,頁面只是局部刷新。 一, 什么是單頁面應(yīng)用 通俗的來講,就是一個(gè)應(yīng)用只有一個(gè)頁面,用戶通過切換路由和動態(tài)獲取數(shù)據(jù)達(dá)到頁面更新的目的,整個(gè)應(yīng)用的使用過程中,頁面只是局部刷新。在整個(gè)應(yīng)用初始加載時(shí),會一次性加載所有靜態(tài)文件或所有公共靜態(tài)文件(切換頁面時(shí),加載相應(yīng)...

    sugarmo 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<