achen的个人博客

一个能力有限的前端


  • 首页

  • 归档49

  • 公益 404

  • 搜索

什么是函数柯里化?

发表于 2019-05-07 | 更新于 2020-05-25

什么是函数柯里化?实现 sum(1)(2)(3) 返回结果是1,2,3之和

函数柯里化:把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数

1
2
3
4
5
6
7
function sum(a) {
return function(b) {
return function(c) {
return a+b+c;
}
}
}

引申:实现一个curry函数,将普通函数进行柯里化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function curry(fn, args = []) {
return function() {
let rest = [...args, ...arguments];

if (rest.length < fn.length) {
return curry.call(this, fn, rest);
} else {
return fn.apply(this, rest);
}
}
}
//test
function sum(a,b,c) {
return a+b+c;
}
let sumFn = curry(sum);
console.log(sumFn(1)(2)(3)); //6
console.log(sumFn(1)(2, 3)); //6

常见的浏览器兼容问题

发表于 2019-05-05 | 更新于 2020-05-25

常见的浏览器兼容问题

html中的兼容问题

不同浏览器的标签默认的外补丁和内补丁不同

  • 场景:随便写几个标签,不加样式控制的情况下,各自的margin和padding差异较大。
  • 解决方法:上来先消除默认样式* {margin: 0, padding: 0}

块属性标签float后,又有横行的margin的情况下,在IE6显示margin比设置的大(即双倍边距bug)

  • 场景:常见症状是IE6后面的一块被顶到下一行;
  • 解决方法:在float的标签样式控制中加入display: inline-block; 将其转化为行内属性

IE6 中z-index失效

  • 场景:元素的父级元素设置的z-index为1,那么其子级元素再设置z-index时会失效,其层级会继承父级元素的设置,造成某些层级调整上的bug
  • 原因:z-index起作用有个前提,就是元素的position属性要是relative、absolute或者fixed。
  • 解决方案:1. position: relative 改为 position: absolute; 2. 去除浮动; 3. 浮动元素添加position属性(如relative,absolute等)。

在写a标签的样式,写的样式没有效果,其实只是写的样式被覆盖了

  • 正确的a标签顺序:link/visited/hover/active

24位png图片,IE6中不兼容透明底儿

  • 解决方式:1. 使用8位png图片; 2. 为ie6准备一套特殊的图片

js在不同浏览器中的兼容问题

事件监听的兼容

  • IE不支持addEventListener;
  • 解决:给IE使用attachEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var addHandler = function(e, type, handler) {
if (e.addEventListener) {
e.addEventListener(type, handler, false);
} else if (e.attachEvent) {
e.attachEvent('on' + type, handler);
} else {
e['on' + type] = handler;
}
}

var removeHandler = function(e, type, handler) {
if (e.removeEventListener) {
e.removeEventListener(type, handler, false);
} else if (e.detachEvent) {
e.detachEvent('on' + type, handler);
} else {
on['on' + type] = null
}
}

event.target的兼容,引发事件的DOM元素。

  • IE 6789不支持event.target
  • 解决方法:event.srcElement;
1
target = event.target || event.srcElement;

阻止系统默认的兼容

  • IE 6789不支持event.preventDefault;
1
event.preventDefault ? event.preventDefault() : event.returnValue = false

阻止事件冒泡的兼容

  • IE 6789不支持event.stopPropagation;
1
event.stopPropagation ? event.stopPropagation() : event.cancelBubble = false

构造方法constructor

发表于 2019-05-03 | 更新于 2020-05-25

构造方法

constructor是一种用于创建和初始化class创建的对象的特殊方法。
描述

  1. 在一个类中只能有一个名叫”constructor”的特殊方法。一个类中出现多次将会抛出SyntaxError错误。
  2. 在一个构造方法中可以使用super关键字来调用一个父类的构造方法
  3. 如果没有显示指定构造方法,则会添加默认的construtor方法。
  4. 如果不指定一个构造函数(constructor)方法,则使用一个默认的构造函数(constructor)

如果不指定构造方法,则使用默认构造函数。对于基类,默认构造函数是:

1
constructor() {}

对于派生类,默认构造函数是:

1
2
3
constructor(...args) {
super(...args);
}

extends

extends关键字用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。
描述

extends关键字用来创建一个普通类或者内建对象的子类
继承的.prototype必须是一个Object或者null。

static

类(class)通过static关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能

1
2
3
4
5
6
class ClassWithStaticMethod {
static staticMethod() {
return 'static method has been called.';
}
}
ClassWithStaticMethod.staticMethod();

调用静态方法

  • 从另一个静态方法,静态方法调用同一个类中的其他静态方法,可使用this关键字
1
2
3
4
5
6
7
8
class StaticMethodCall {
static staticMethod() {
return 'Static method has been called';
}
static anotherStaticMethod() {
return this.staticMethod() + ' from another static method';
}
}
  • 从类的构造函数和其他方法,非静态方法中,不能直接使用this关键字来访问静态方法。而是要用类名来调用;
    CLASSNAME.STATIC_METHOD_NAME(),或者用构造函数的属性来调用该方法;this.constructor.STATIC_METHOD_NAME()。
1
2
3
4
5
6
7
8
9
10
11
class StaticMethodCall {
constructor() {
console.log(StaticMethodCall.staticMethod());
// 'static method has been called.'
console.log(this.constructor.staticMethod());
// 'static method has been called.'
}
static staticMethod() {
return 'static method has been called.';
}
}

补充

  1. 目前ECMAScript,class中还没有定义Private(私有属性)的能力,所以我们通过约定,用下划线来标记它们。
1
2
3
4
5
6
7
8
9
class Car {
_milesDriven = 0;
drive(distance) {
this._milesDriven += distance;
}
getMilesDriven() {
return this._milesDriven;
}
}

在上面的事例中,我们依靠Car的实例调用getMilesDriven方法来获取到它的私有属性_milesDriven。但是,因为没有什么能使_milesDriven成为私有的,
所以任何实例都可以访问它。

1
2
3
const tesla = new Cal();
tesla.drive(10);
console.log(tesla._milesDriven);

目前Class Fields有个提案,我们可以通过#创建私有字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Car {
#milesDriven = 0;
drive(distance) {
this.#milesDriven += distance;
}
getMilesDriven() {
return this.#milesDriven;
}
}

const tesla = new Car()
tesla.drive(10)
tesla.getMilesDriven() // 10
tesla.#milesDriven // Invalid

call、apply有什么区别?call、apply和bind的内部实现

发表于 2019-05-01 | 更新于 2020-05-25

call、apply有什么区别?call、apply和bind的内部实现

call、apply的功能相同,区别在于传参的方式不一样:

  • fn.call(obj, arg1, arg2, …),调用一个函数,具有一个指定的this值和分别提供的参数(参数的列表)。
  • fn.apply(obj, [argsArray]),调用一个函数,具有一个指定的this值,以及作为一个数组(或类数组对象)提供参数。

call核心

  • 将函数设为传入参数的属性
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数或者参数为null,默认指向为 window/global
  • 删除参数上的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Function.prototype.call = function(context) {
/*
* 如果第一个参数传入的是 null 或者 undefined,那么this指向 window/global
* 如果第一个参数传入的不是 null 或者 undefined,那么必须是一个对象
*/
if (!context) {
// context为null或者是undefined
// 判断是浏览器环境还是node环境
context = typeof window === 'undefined' ? global : window;
}
context.fn = this; // this指向是当前的函数(Function的实例)
let rest = [...arguments].slice(1); // 获取除了this指向对象以外的参数,空数组slice后返回的仍然是空数组
let result = context.fn(...rest); // 隐式绑定,当前函数的this指向了context
delete context.fn;
return result;
}

// test code
var foo = {
name: 'Selina'
};
var name = 'Chirs';
function bar(job, age) {
console.log(this.name);
console.log(job, age);
}
bar.call(foo, 'programmer', 20);
// Selina programmer, 20
bar.call(null, 'teacher', 25);
// brower环境: Chirs teacher, 25; node环境: undefined teacher, 25

apply核心

apply的实现和call很类似,但是需要注意他们的参数不一样,apply的第二个参数是数组或类数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Function.prototype.apply = function(context, rest) {
/*
* 如果第一个参数传入的是 null 或者 undefined,那么this指向 window/global
* 如果第一个参数传入的不是 null 或者 undefined,那么必须是一个对象
*/
if (!context) {
// context为null或者是undefined
// 判断是浏览器环境还是node环境
context = typeof window === 'undefined' ? global : window;
}
context.fn = this;
let result;
if (rest == null) {
// undefined 或者 null 不是 Iterator 对象,不能被...
result = context.fn(rest);
} else if (typeof rest === 'object') {
result = context.fn(...rest);
}

delete result.fn;
return result;
}

// test code
var foo = {
name: 'Selina'
};
var name = 'Chirs';
function bar(job, age) {
console.log(this.name);
console.log(job, age);
}
bar.apply(foo, ['programmer', 20]);
// Selina programmer, 20
bar.apply(null, ['teacher', 25]);
// brower环境: Chirs teacher, 25; node环境: undefined teacher, 25

bind核心

bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind会创建一个新函数。当这个新函数被调用时,bind()的第一个参数将
作为它运行时的this,之后的一系列参数将会在传递的实参前传入作为它的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Function.prototype.bind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
let self = this;
let args = [...arguments].slice(1);
function Fn() {};
Fn.prototype = this.prototype;
let bound = function() {
let res = [...args, ...arguments]; // bind传递的参数和函数调用时传递的参数拼接
context = this instanceof Fn ? this : context || this;
return self.apply(context, res);
}

// 原型链
bound.prototype = new Fn();
return bound;
}

var name = 'Jack';
function person(age, job, gender){
console.log(this.name , age, job, gender);
}
var Yve = {name : 'Yvette'};
let result = person.bind(Yve, 22, 'enginner')('female');

fileReader读取文件,实现图片本地预览

发表于 2018-10-09 | 更新于 2020-09-18

FileReader

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。

首先创建一个FileReader实例:

1
var reader = new FileReader();

方法

方法 描述
abort() 中止读取操作。在返回时,readyState属性为DONE。
readAsArrayBuffer() 开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象.
readAsBinaryString() 开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。
readAsDataURL() 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容
readAsText() 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。

具体事例

input file实现本地图片预览

1
2
3
4
5
6
7
8
9
var inputBox = document.getElementById("inputBox");
inputBox.addEventListener("change",function(){
var reader = new FileReader();
reader.readAsDataURL(inputBox.files[0]);//发起异步请求
reader.onload = function(){
// 读取完成后,base64数据保存在对象的result属性中
console.log(this.result)
}
})

引用

  • FileReader详解

react-router解读(三)

发表于 2018-08-28 | 更新于 2018-08-29

history methods createBrowserHistory

通过之前我们已经知道了react-router-dom的 history模式使用的是history库中的createBrowserHistory方法。下面我们来一起看看其中的奥秘。

  var createBrowserHistory = function createBrowserHistory() {
    //  arguments[0], 由上一节可知它是一个非必传的object
    var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

    (0, _invariant2.default)(_DOMUtils.canUseDOM, 'Browser history needs a DOM');
    //  之前将history赋值给变量globalHistory
    var globalHistory = window.history;
    //  _DOMUtils.js 中处理一些浏览器兼容性 和 浏览器内核、机型判断的方法
    //  判断是否非主流机型以及浏览器
    var canUseHistory = (0, _DOMUtils.supportsHistory)();
    //  是否为IE浏览器
    var needsHashChangeListener = !(0, _DOMUtils.supportsPopStateOnHashChange)();
    //  forceRefresh为true,代表将在页面导航时使用完整页面刷新。其他参数请自行参考官网文档
    var _props$forceRefresh = props.forceRefresh,
        forceRefresh = _props$forceRefresh === undefined ? false : _props$forceRefresh,
        _props$getUserConfirm = props.getUserConfirmation,
        getUserConfirmation = _props$getUserConfirm === undefined ? _DOMUtils.getConfirmation : _props$getUserConfirm,
        _props$keyLength = props.keyLength,
        keyLength = _props$keyLength === undefined ? 6 : _props$keyLength;

    var basename = props.basename ? (0, _PathUtils.stripTrailingSlash)((0, _PathUtils.addLeadingSlash)(props.basename)) : '';

    var getDOMLocation = function getDOMLocation(historyState) {
      var _ref = historyState || {},
          key = _ref.key,
          state = _ref.state;

      var _window$location = window.location,
          pathname = _window$location.pathname,
          search = _window$location.search,
          hash = _window$location.hash;

      var path = pathname + search + hash;

      (0, _warning2.default)(!basename || (0, _PathUtils.hasBasename)(path, basename), 'You are attempting to use a basename on a page whose URL path does not begin ' + 'with the basename. Expected path "' + path + '" to begin with "' + basename + '".');

      if (basename) path = (0, _PathUtils.stripBasename)(path, basename);

      return (0, _LocationUtils.createLocation)(path, state, key);
    };

    var createKey = function createKey() {
      return Math.random().toString(36).substr(2, keyLength);
    };

    var transitionManager = (0, _createTransitionManager2.default)();

    var setState = function setState(nextState) {
      _extends(history, nextState);

      history.length = globalHistory.length;

      transitionManager.notifyListeners(history.location, history.action);
    };

    var handlePopState = function handlePopState(event) {
      if ((0, _DOMUtils.isExtraneousPopstateEvent)(event)) return;

      handlePop(getDOMLocation(event.state));
    };

    var handleHashChange = function handleHashChange() {
      handlePop(getDOMLocation(getHistoryState()));
    };

    var forceNextPop = false;

    var handlePop = function handlePop(location) {
      if (forceNextPop) {
        forceNextPop = false;
        setState();
      } else {
        var action = 'POP';

        transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
          if (ok) {
            setState({ action: action, location: location });
          } else {
            revertPop(location);
          }
        });
      }
    };

    var revertPop = function revertPop(fromLocation) {
      var toLocation = history.location;

      // TODO: We could probably make this more reliable by
      // keeping a list of keys we've seen in sessionStorage.
      // Instead, we just default to 0 for keys we don't know.

      var toIndex = allKeys.indexOf(toLocation.key);

      if (toIndex === -1) toIndex = 0;

      var fromIndex = allKeys.indexOf(fromLocation.key);

      if (fromIndex === -1) fromIndex = 0;

      var delta = toIndex - fromIndex;

      if (delta) {
        forceNextPop = true;
        go(delta);
      }
    };

    var initialLocation = getDOMLocation(getHistoryState());
    var allKeys = [initialLocation.key];

    var createHref = function createHref(location) {
      return basename + (0, _PathUtils.createPath)(location);
    };

    var push = function push(path, state) {
      (0, _warning2.default)(!((typeof path === 'undefined' ? 'undefined' : _typeof(path)) === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to push when the 1st ' + 'argument is a location-like object that already has state; it is ignored');

      var action = 'PUSH';
      //  重写location对象
      var location = (0, _LocationUtils.createLocation)(path, state, createKey(), history.location);

      transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
        if (!ok) return;

        var href = createHref(location);
        var key = location.key,
            state = location.state;

        if (canUseHistory) {
          globalHistory.pushState({ key: key, state: state }, null, href);

          if (forceRefresh) {
            window.location.href = href;
          } else {
            var prevIndex = allKeys.indexOf(history.location.key);
            var nextKeys = allKeys.slice(0, prevIndex === -1 ? 0 : prevIndex + 1);

            nextKeys.push(location.key);
            allKeys = nextKeys;

            setState({ action: action, location: location });
          }
        } else {
          (0, _warning2.default)(state === undefined, 'Browser history cannot push state in browsers that do not support HTML5 history');

          window.location.href = href;
        }
      });
    };

    var replace = function replace(path, state) {
      (0, _warning2.default)(!((typeof path === 'undefined' ? 'undefined' : _typeof(path)) === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to replace when the 1st ' + 'argument is a location-like object that already has state; it is ignored');

      var action = 'REPLACE';
      var location = (0, _LocationUtils.createLocation)(path, state, createKey(), history.location);

      transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
        if (!ok) return;

        var href = createHref(location);
        var key = location.key,
            state = location.state;


        if (canUseHistory) {
          globalHistory.replaceState({ key: key, state: state }, null, href);

          if (forceRefresh) {
            window.location.replace(href);
          } else {
            var prevIndex = allKeys.indexOf(history.location.key);

            if (prevIndex !== -1) allKeys[prevIndex] = location.key;

            setState({ action: action, location: location });
          }
        } else {
          (0, _warning2.default)(state === undefined, 'Browser history cannot replace state in browsers that do not support HTML5 history');

          window.location.replace(href);
        }
      });
    };

    var go = function go(n) {
      globalHistory.go(n);
    };

    var goBack = function goBack() {
      return go(-1);
    };

    var goForward = function goForward() {
      return go(1);
    };

    var listenerCount = 0;

    var checkDOMListeners = function checkDOMListeners(delta) {
      listenerCount += delta;

      if (listenerCount === 1) {
        (0, _DOMUtils.addEventListener)(window, PopStateEvent, handlePopState);

        if (needsHashChangeListener) (0, _DOMUtils.addEventListener)(window, HashChangeEvent, handleHashChange);
      } else if (listenerCount === 0) {
        (0, _DOMUtils.removeEventListener)(window, PopStateEvent, handlePopState);

        if (needsHashChangeListener) (0, _DOMUtils.removeEventListener)(window, HashChangeEvent, handleHashChange);
      }
    };

    var isBlocked = false;

    var block = function block() {
      var prompt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;

      var unblock = transitionManager.setPrompt(prompt);

      if (!isBlocked) {
        checkDOMListeners(1);
        isBlocked = true;
      }

      return function () {
        if (isBlocked) {
          isBlocked = false;
          checkDOMListeners(-1);
        }

        return unblock();
      };
    };

    var listen = function listen(listener) {
      var unlisten = transitionManager.appendListener(listener);
      checkDOMListeners(1);

      return function () {
        checkDOMListeners(-1);
        unlisten();
      };
    };

    var history = {
      length: globalHistory.length,
      action: 'POP',
      location: initialLocation,
      createHref: createHref,
      push: push,
      replace: replace,
      go: go,
      goBack: goBack,
      goForward: goForward,
      block: block,
      listen: listen
    };

    return history;
  };

react-router解读(二)

发表于 2018-08-27 | 更新于 2018-08-29

react-router 的路由实现

既然知道了这个原理,我们来看下 react-router 的实现,我们打开 react-router 项目地址,把项目克隆下来,或则直接在 github 上预览,在 React 开发的项目里,我们通过 router.push(‘home’) 来实现页面的跳转,所以我们检索下,push 方法的实现

push方法检索

我们检索到了 46个 js 文件,😂,一般到这个时候,我们会放弃源码阅读,那么我们今天的文章就到这结束,谢谢大家!

开个玩笑,源码阅读不能这么粗糙,react-router 4.x用法,我们只需要安装 react-router-dom。所以我们找到 package 下的 react-router-dom/modules 目录,点开 index.js 文件。

  export BrowserRouter from "./BrowserRouter";
  export HashRouter from "./HashRouter";
  export Link from "./Link";
  export MemoryRouter from "./MemoryRouter";
  export NavLink from "./NavLink";
  export Prompt from "./Prompt";
  export Redirect from "./Redirect";
  export Route from "./Route";
  export Router from "./Router";
  export StaticRouter from "./StaticRouter";
  export Switch from "./Switch";
  export generatePath from "./generatePath";
  export matchPath from "./matchPath";
  export withRouter from "./withRouter";

看到 history 对象的实例与配置的 mode 有关,react-router-dom 通过3种方式实现了路由切换。我们今天讲的内容相匹配的是 createBrowserHistory 的实现方案 (另外两个分别是:createHashHistory和createMemoryHistory)。 这里 react-router-dom 将 BrowserRouter、HashRouter、Link 拆分为单独组件实现,也是与 react-router 3 之间的区别之一。
详细文档
我们来看 react-router-dom 中的 BrowserRouter 源码:

    import warning from "warning";
    import React from "react";
    import PropTypes from "prop-types";
    import { createBrowserHistory as createHistory } from "history";
    import Router from "./Router";

    /**
    * The public API for a  that uses HTML5 history.
    */
    class BrowserRouter extends React.Component {
      static propTypes = {
        basename: PropTypes.string,
        forceRefresh: PropTypes.bool,
        getUserConfirmation: PropTypes.func,
        keyLength: PropTypes.number,
        children: PropTypes.node
      };

      history = createHistory(this.props);

      componentWillMount() {
        warning(
          !this.props.history,
          " ignores the history prop. To use a custom history, " +
            "use `import { Router }` instead of `import { BrowserRouter as Router }`."
        );
      }

      render() {
        return ;
      }
    }

    export default BrowserRouter;

由上可知我们在项目中使用的组件,history方式是使用了一个叫history库中的 createBrowserHistory 方法。

模拟单页面路由

通过上面的学习,我们知道了,单页面应用路由的实现原理,我们也尝试去实现一个。在做管理系统的时候,我们通常会在页面的左侧放置一个固定的导航 sidebar,页面的右侧放与之匹配的内容 main 。点击导航时,我们只希望内容进行更新,如果刷新了整个页面,到时导航和通用的头部底部也进行重绘重排的话,十分浪费资源,体验也会不好。这个时候,我们就能用到我们今天学习到的内容,通过使用 HTML5 的 pushState 方法和 replaceState 方法来实现,

思路:首先绑定 click 事件。当用户点击一个链接时,通过 preventDefault 函数防止默认的行为(页面跳转),同时读取链接的地址(如果有 jQuery,可以写成$(this).attr(‘href’)),把这个地址通过pushState塞入浏览器历史记录中,再利用 AJAX 技术拉取(如果有 jQuery,可以使用$.get方法)这个地址中真正的内容,同时替换当前网页的内容。

为了处理用户前进、后退,我们监听 popstate 事件。当用户点击前进或后退按钮时,浏览器地址自动被转换成相应的地址,同时popstate事件发生。在事件处理函数中,我们根据当前的地址抓取相应的内容,然后利用 AJAX 拉取这个地址的真正内容,呈现,即可。

最后,整个过程是不会改变页面标题的,可以通过直接对 document.title 赋值来更改页面标题。

扩展

好了,我们今天通过多个方面来讲了 pushState 方法和 replaceState 的应用,你应该对这个两个方法能有一个比较深刻的印象,如果想要了解更多,你可以参考以下链接

history对象

react-router解读(一)

发表于 2018-08-27 | 更新于 2020-09-18

前言

前端路由是通过改变URL,在不重新请求页面的情况下,更新页面视图。
目前在浏览器环境中这一功能的实现主要有2种:

  • 利用URL中的hash
  • 利用H5中history

pushState 和 replaceState 了解一下

history 提供了两个方法,能够无刷新的修改用户的浏览记录,pushSate,和 replaceState,区别的 pushState 在用户访问页面后面添加一个访问记录, replaceState 则是直接替换了当前访问记录

history 对象的详细信息已经有很多很好很详细的介绍文献,这里不再做总结history对象

history.pushState

history.pushState方法接受三个参数,依次为:

state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。
title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。
url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
假定当前网址是example.com/1.html,我们使用pushState方法在浏览记录(history对象)中添加一个新记录。

    var stateObj = { foo: 'bar' };
    history.pushState(stateObj, 'page 2', '2.html');

添加上面这个新记录后,浏览器地址栏立刻显示 example.com/2.html,但并不会跳转到 2.html,甚至也不会检查2.html 是否存在,它只是成为浏览历史中的最新记录。这时,你在地址栏输入一个新的地址(比如访问 google.com ),然后点击了倒退按钮,页面的 URL 将显示 2.html;你再点击一次倒退按钮,URL 将显示 1.html。

总之,pushState 方法不会触发页面刷新,只是导致 history 对象发生变化,地址栏会有反应。

如果 pushState 的 url参数,设置了一个新的锚点值(即hash),并不会触发 hashchange 事件。如果设置了一个跨域网址,则会报错。

    // 报错
    history.pushState(null, null, 'https://twitter.com/hello');

    上面代码中,pushState想要插入一个跨域的网址,导致报错。这样设计的目的是,防止恶意代码让用户以为他们是在另一个网站上。

history.replaceState

history.replaceState 方法的参数与 pushState 方法一模一样,区别是它修改浏览历史中当前纪录,假定当前网页是 example.com/example.html。

    history.pushState({page: 1}, 'title 1', '?page=1');
    history.pushState({page: 2}, 'title 2', '?page=2');
    history.replaceState({page: 3}, 'title 3', '?page=3');

    history.back()
    // url显示为http://example.com/example.html?page=1

    history.back()
    // url显示为http://example.com/example.html

    history.go(2)
    // url显示为http://example.com/example.html?page=3

Hello Hexo

发表于 2018-08-24 | 更新于 2018-08-29

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

1…45
achen

achen

日常复制粘贴,问啥啥不会

49 日志
12 标签
© 2021 achen
由 Hexo 强力驱动 v3.7.1
|
主题 – NexT.Pisces v6.4.0