前端面试六

26、用js或jQuery获取id为container的div里边的所有a标签,并把字体大小设置为18px;同 时把第三个a标签设置为块级元素,并把他的宽高分别设置为300px和500px。 这里主要是第几个子元素使用css属性nth-child(index),index从1开始

 

27、请用jquery和原生js分别实现创建、添加、复制、移除、移动、和查找DOM结点

● js 创建DOM节点,并且添加到DOM中

document.createTextNode("sss")//创建文本节点
document.createElement("p")//创建元素节点
document.createDocumentFragment();//创建文档碎片节点
//insertBefore() 方法,如果不是在末尾插入节点,而是想放在特定的位置上,用这个方法,
//该方法接受2个参数,第一个是要插入的节点,第二个是参照节点,返回要添加的元素节点
 var ul = document.getElementById("myList"); //获得ul
 var li = document.createElement("li"); //创建li
 li.innerHTML= "项目四"; //向li内添加文本
 ul.insertBefore(li,ul.firstChild); //把li添加到ul的第一个子节点前
 appendChild()方法。添加在末尾
 //为body添加一个内容为sss的文本节点
document.body.appendChild(document.createTextNode("sss"));

● jQuery 创建DOM节点,并且添加到DOM中

<!-- jQuery创建DOM节点 -->
var str = $("<a href='http://www.baidu.com'>百度</a>"); 
<!--append 追加在父元素的最后一个子节点后面
prepend插入到父元素的第一个子节点前面
after在元素后面追加,同级
befor在元素的前面追加,同级  -->
$("ul").append(str); //将动态创建的str元素做为ul的最后一个子节点
$("ul").prepend(str); //将动态创建的str元素做为ul的第一个子节点
$("ul").after(str); //将动态创建的str元素放在ul的下面,两者是堂兄弟的关系
$("ul").before(str); //将动态创建的str元素放在ul的上面,两者是堂兄弟的关系

● js移除DOM节点,removeChild()移除某个节点的子节点

var ccn=document.getElementById("sd").childNodes[0];//获取到要移除的节点
document.getElementById("sd").removeChild(ccn);//将id为sd的第一个子节点移除

● jQuery移除DOM节点

<!-- remove() - 删除被选元素(及其子元素) -->
<!-- empty() - 从被选元素中删除子元素 -->
$('ul').remove();//删除ul及其子元素
$('ul').empty();//删除ul的子元素,不包括ul,返回被删除的 元素

● js移动DOM节点

//html如下
<div id='sd' style="position: absolute; left: 10px;">sd</div>
// 将id为sd的节点向右边移动50px   通过style属性只能获取到行内元素样式,多行元素获取到的是udnefined,通过classList属性可以获取多行元素样式属性
var sdds = document.getElementById("sd"); 
sdds.style.left = parseInt(sdds.style.left) + 50 + "px";

● js查找DOM节点

document.getElementById('oo');//根据id查找
document.getElementsByTagName("p");//根据标签名字查找节点
document.getElementsByClassName("one")//根据className

● jQuery查找DOM节点

$('#name') //根据id查找
$('.color')//根据类名查找
$('p')//根据tagName查找

● js复制DOM节点  cloneNode()

//复制节点
//cloneNode() 方法,用于复制节点, 接受一个布尔值参数,
// true 表示深复制(复制节点及其所有子节点), false
// 表示浅复制(复制节点本身,不复制子节点)

var ul = document.getElementById("myList"); //获得ul
var deepList = ul.cloneNode(true); //深复制
var shallowList = ul.cloneNode(false); //浅复制

● jQuery复制DOM节点 clone()

$('ul').clone(true).appendTo("p"); // 复制ul节点,并将它追加到<p>元素 

js怎样获取div的id为‘content’的文本内容

document.getElementById('content').innerHTML
document.getElementById('content').innerText
document.getElementById('content').value

js获取节点的名称

document.getElementById('content').nodeName

28、jquery怎样获取div的id为‘content’的文本内容

$('#content').text();//获取文本的内容
$('#content').html();//获取html的内容,包括标签
$('#con').val();//获取<input> <textarea>的值

29、你怎么看AMD vs.Commonjs?

共同点:两者都是为了实现模块化编程而出现的。对于大型项目,参与人数多,代码逻辑复杂,是最适合使用模块化的思想来完成整个项目的。同时采用这种思想也很便于对整个项目进行管控

区别

CommonJS:

● 适用于服务器端Node.js执行环境就是采用CommonJS模式

● 同步加载不同模块文件。之所以采用同步加载方式,是因为模块文件都存放在服务器的各个硬盘上,实际的加载时间就是硬盘的文件读取时间

AMD,Asynchronous Module Definition,即异步模块定义

● 适用于浏览器端的一种模块加载方式

● AMD采用的是异步加载方式(js中最典型的异步例子就是ajax)

● 浏览器需要使用的js文件(第一次加载,忽略缓存)都存放在服务器端,从服务器端加载文件到浏览器是受网速等各种环境因素的影响的,如果采用同步加载方式,一旦js文件加载受阻,后续在排队等待执行的js语句将执行出错,会导致页面的‘假死’,用户无法使用一些交互。所以在浏览器端是无法使用CommonJS的模式的。

目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js

下面程序输出的结果,以及改进的方法

for( var i=0;i<3;i++ ){
   setTimeout(function(){console.log(i)},0);  //3次3
}

29、ES6模块和CommonJS模块的差异

1)ES6输入的模块变量,只是一个符号链接,所以这个变量是只读的,对它重新进行赋值会报错

2)CommonJS模块输出的是一个值的拷贝,ES6模块输出的是一个值的引用

3)CommonJS模块是运行时加载,ES6模块是编译时输出接口

29、模块化加载的顺序如何

1)commonjs:同步、顺序执行

2)AMD:提前加载,不管是否调用模块,先解析所有模块,requirejs的速度快,有可能浪费资源

3)CMD: 提前加载,在真正需要使用(依赖)模块时才解析该模块,seajs 按需解析,性能比AMD差

30、Requirejs的使用过程是怎样的(怎样搭建一个模块化项目)?

什么是Requirejs?

● RequireJS是一个非常小巧的JavaScript模块载入框架

● 是AMD规范最好的实现者之一

● 最新版本的RequireJS压缩后只有14K,堪称非常轻量

● 它还同时可以和其他的框架协同工作

● 使用RequireJS必将使您的前端代码质量得以提升

使用RequireJS能带来什么样的好处?为什么使用RequireJS

传统的引入js文件的形式是连续加载多个js文件,如下面的形式:

  <script src="1.js"></script>
  <script src="2.js"></script>
  <script src="3.js"></script>
  <script src="4.js"></script>
  <script src="5.js"></script>
  <script src="6.js"></script>

上面的写法存在两点缺陷

 加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长

● 由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面)。依赖性最大的模块一定要放到最后加载,当依赖关系复杂的时候,代码的编写和维护都会变得困难

使用RequireJS就是为了解决上面的问题

● 实现js文件的异步加载,避免网页失去响应
● 管理模块之间的依赖性,便于代码的编写和维护
● 可以按需、并行、延时载入js库

RequireJS搭建一个模块化项目的过程

a) 在html中引入Requirejs

<script data-main="js/app.js" src="js/require.js"></script>

data-main的作用是指定网页模块的主程序,即对应的路径是Requirejs配置的主入口, data-main和 src中路径都是相对html的路径,或者是绝对路径。

b) 主模块(整个网页的入口代码,所有的代码都从这运行)的写法,即data-main指定的文件的内容

requirejs.config({ //模块的加载
  baseUrl:'js/lib',
  paths :{
      app:'../app'
  }
});

require(["app/start"], function(app) { //主模块
     app.hello();
});

●  baseUrl模块(module)的根目录,如果require(依赖) 的模块直接是用文件名作为id的话,会直接在这个目录寻找同名文件资源
● paths 中的属性可以给不同的路径或者文件别名,如果require(依赖) 的模块使用路径作为id的时候可以通过别名匹配path中路径

c) 定义模块,采用AMD规范,即用define()定义模块。(requirejs提供了多种定义模块的方式,可以使用/不使用依赖返回变量,返回对象,函数,可使用CommonJs的方法在export,module中返结果)

 define(['myLib'], function(myLib){

    function foo(){

      myLib.doSomething();

    }

    return {

      foo : foo

    };

  });

d) shim配置:将某个依赖中的某个全局变量暴露给requirejs,当作这个模块本身的引用

如官网中例子,把backbone.js引入,抛出Backbone作为模块名引入,deps中申明它需要的依赖,backbone依赖underscore 和jquery

requirejs.config({
   shim: {
      'backbone': {
         deps: ['underscore', 'jquery'],
         exports: 'Backbone'
      },
    'underscore': {
        exports: '_'
     }
   }
});

e) 打包压缩:Require.js官网提供了r.js打包工具,只要配置对应的Build.js就可以帮助我们自动化构建

build.js配置参考:链接         官网配置说明:链接

详细说明链接

31、请谈谈你都使用过哪些javascript模板

react.js    vue.js    angular.js     jquery  

32、svg 与canvas的不同

svg与canvas的共同点:

都允许在浏览器中创建图形

SVG的定义:

● SVG 是一种使用 XML 描述 2D 图形的语言

● SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的。您可以为某个元素附加 JavaScript 事件处理器。
● 在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形

Canvas定义:

● Canvas 通过 JavaScript 来绘制 2D 图形

● Canvas 是逐像素进行渲染的

● 在 canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象

svg与canvas的不同点:

Canvas:                                                                                                   

● 依赖分辨率

● 不支持事件处理器

● 弱的文本渲染能力

● 能够以 .png 或 .jpg 格式保存结果图像

● 最适合图像密集型的游戏,其中的许多对象会被频繁重绘

svg:       

● 不依赖分辨率

● 支持事件处理器

● 最适合带有大型渲染区域的应用程序(比如谷歌地图)

● 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)

● 不适合游戏应用

37、Promise的实现原理

https://zhuanlan.zhihu.com/p/25178630     解释1

https://juejin.im/post/5a30193051882503dc53af3c    解释2

大致思路

1、Promise的构造函数,只有一个函数参数resolver(resolve,reject),而函数参数resolver的两个参数都是函数参数,第一个参数resolve表示处理成功执行的函数,reject表示失败执行的函数

在构造函数中的属性state(保存当前promise的状态);value(保存resolve或发生reject的错误原因);queue(Array,同一个promise可以多次调用then方法,这里是保存调用then方法之后生成的promise对象及其对应的resolve  reject函数)

构造函数主要职责有:

a)初始化Promise的状态,this.state = PENDING

b)初始化resolve或reject的参数值,this.value = void 0

c)初始化then()注册的回调函数,this.queue = []

d)立即执行resolver(resolve,reject),即调用函数safelyResolveThen(self, then)-

立即执行resolver(resolve,reject)被封装在函数safelyResolveThen(self, then)----self为当前promise对象,resolver=then

这里就是执行函数resolver.

通过闭包对变量called的持有,使变量一直保存在内存中,达到resolve   reject只执行一次,即同一个promise的状态由pending=>fulfilled或者pending => rejected只发生一次

在函数safelyResolveThen(self, then)里面调用了全局函数doResolve(self, value)和doReject(self, error)

doResolve(self, value)---doResolve 结合 safelyResolveThen 使用,不断地解包 promise 对象后,当值不是一个thenable对象时设置 promise 的状态和值。解包promise对象使用了方法getThen(value)。同时对queue遍历,通知queue中的每个promise的子promise,子promise 根据传入的值设置自己的状态和值

getThen(value)----如果value是一个thenable对象函数,那么就返回一个函数(在函数内,通过apply将value.then方法的作用域改成value,并执行then方法)

doReject(self, error) -----设置 promise的state为 REJECTED,value 为 error。同时遍历queue,对每个成员对象调用 callRejected,通知子 promise,子 promise 根据传入的错误设置自己的状态和值

2、Promise.prototype.then(onFulfilled, onRejected)----两个参数,成功时执行的回调函数onFulfilled,失败时执行的回调函数

onRejected。返回值是一个新生成的promise对象

根据当前promise的state状态来做相应的处理

1)存在值穿透问题,即then方法没有参数或者参数为一个值

2)state不为PENDING,则执行unwrap(promise, resolver, this.value)。判断state为FULFILLED则resolver=onFulfilled,为REJECTED则resolver=onRejected。

3)否则promise的状态没有改变,即state=PENDIGN,将生成的promise对象和对应的onFulfilled, onRejected加入到当前promise的回调队列queue里面

unwrap(promise, resolver, this.value)----调用immediate.js将同步代码生成异步代码。同时执行回调函数,并验证处理的结果returnValue,如果为promise本身则抛出错误,否则调用doResolve(promise, returnValue)

Promise的静态方法

3、Promise.resolve(value)------返回一个promise对象;

a)value是一个promise对象则直接返回value;否则调用doResolve函数

b)value是一个thenable对象,将其转为promise对象,立即执行then方法,该promise的状态由这个thenable对象的then方法的具体设置而定;

c)value是一个原始值,或者是一个不具有then方法的对象,返回promise对象,状态为resolve

d)不带任何参数,直接返回一个promise对象

4、Promise.reject(reason)----返回一个promise对象;该实例的状态为rejected

直接返回doReject(promise, reason)处理后的结果

5、Promise.all(iterable)-------接受一个参数,返回一个promise。将多个promise封装成一个promise

返回promise的回调函数的参数值:

a)只有iterable中所有的promise对象的状态都变成fulfilled的时候,返回promise的状态才为fulfilled,且传递给返会promise对象的回调函数的参数为Array,元素由这些promise  resolve之后的值组成

b)否则返回第一个被reject的实例的返回值,作为返回promise的回到函数的参数

6、Promise.race(iterable)-------接受一个参数,返回一个promise。将多个promise封装成一个promise

返回promise的回调函数的参数值:

a)返回第一个状态先改变的promise对象处理之后的值

'use strict';

var immediate = require('immediate');  //用immediate.js库将同步代码装成异步代码

function INTERNAL() {}

function isFunction(func) {
  return typeof func === 'function';
}

function isObject(obj) {  //Array   Object  Null  Set   Map 都为true
  return typeof obj === 'object';   
}

function isArray(arr) {
  return Object.prototype.toString.call(arr) === '[object Array]';
}

var PENDING = 0;    //控制promise对象的状态
var FULFILLED = 1;
var REJECTED = 2;

module.exports = Promise;  //输出模块

function Promise(resolver) {   //Promise构造函数
  if (!isFunction(resolver)) {
    throw new TypeError('resolver must be a function');
  }
  this.state = PENDING;  //初始化promise的状态为PENDIGN
  this.value = void 0;   //初始化resolve或reject的参数值
  this.queue = [];       //then方法可被同一个promise对象多次调用,用于注册新生成的promise对象
  if (resolver !== INTERNAL) {  
    safelyResolveThen(this, resolver);  //立即执行传进来的resolver函数
  }
}

function safelyResolveThen(self, then) {
  var called = false;  //called被用在闭包里面,使得它的值一直保存在内存中,用于promise的
  try {                 //called 控制 resolve 或 reject 只执行一次,多次调用没有任何作用
    then(function (value) {
      if (called) {
        return;
      }
      called = true;
      doResolve(self, value);
    }, function (error) {
      if (called) {
        return;
      }
      called = true;
      doReject(self, error);
    });
  } catch (error) {
    if (called) {
      return;
    }
    called = true;
    doReject(self, error);
  }
}

function doResolve(self, value) {  //doResolve 结合 safelyResolveThen 使用,不断地解包 
                                   // promise 对象后,设置 promise 的状态和值
  try {
    var then = getThen(value);
    if (then) {
      safelyResolveThen(self, then);  //value是一个thenable对象,则递归遍历直到是一个值
    } else {
      self.state = FULFILLED;
      self.value = value;
      self.queue.forEach(function (queueItem) {  //对queue遍历,执行当前promise对应的callFulfilled()
        queueItem.callFulfilled(value);  //callFulfilled通知子 promis, 子promise 根据传入的值设置自己的状态和值。
      });
    }
    return self;     //返回promise对象
  } catch (error) {
    return doReject(self, error);
  }
}

function doReject(self, error) {  // 设置 promise的state为 REJECTED,value 为 error
  self.state = REJECTED;
  self.value = error;
  self.queue.forEach(function (queueItem) {
    queueItem.callRejected(error);  // callRejected通知子 promise,子 promise 根据传入的错误设置自己的状态和值
  });
  return self; //返回当前promise对象
}

function getThen(obj) {  //传入的obj不为空,然后访问obj是否有then方法,有就返回obj.then(),否则为undefined
  var then = obj && obj.then;   // obj是一个thenable对象
  if ( obj && ( isObject(obj) || isFunction(obj) ) && isFunction(then) ) {
    return function appyThen() {
      then.apply(obj, arguments);
    };
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  if( !isFunction(onFulfilled) && this.state === FULFILLED ||
    !isFunction(onRejected) && this.state === REJECTED) {
    return this;   //实现了值穿透
  }

  // 生成新的promise对象
  var promise = new this.constructor(INTERNAL);

  //  promise 的状态改变了,则调用 unwrap
  if (this.state !== PENDING) {
    var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
    unwrap(promise, resolver, this.value);
  } else {
    //promise对象状态没有改变,将生成的promise对象加入到当前promise的回调队列queue里面
    this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
  }
  return promise;
};

Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
};

function unwrap(promise, func, value) { //解包,参数分别为子promise  父promise的then的回调onFulfilled/onRejected
  immediate(function () {     // immediate()将同步代码变成异步代码          //父 promise 的值(正常值/错误)
    var returnValue;
    try {                 //捕获promise.then()   promise.catch()内部的异常
      returnValue = func(value);
    } catch (error) {
      return doReject(promise, error);
    }

    if (returnValue === promise) {  //返回的值不能是 promise 本身,否则会造成死循环
      doReject(promise, new TypeError('Cannot resolve promise with itself'));
    } else {
      doResolve(promise, returnValue);
    }
  });
}


// 类QueueItem具有的属性this.promise  this.callFulfilled   this.callRejected
function QueueItem(promise, onFulfilled, onRejected) {
  this.promise = promise;

  this.callFulfilled = function (value) {
    doResolve(this.promise, value);  //设置当前promise的状态和值
  };

  this.callRejected = function (error) {
    doReject(this.promise, error);
  };

  if (isFunction(onFulfilled)) {
    this.callFulfilled = function (value) {
      unwrap(this.promise, onFulfilled, value);
    };
  }
  if (isFunction(onRejected)) {
    this.callRejected = function (error) {
      unwrap(this.promise, onRejected, error);
    };
  }
}

Promise.resolve = resolve;
function resolve(value) {  //返回一个promise对象
  if (value instanceof this) {  //当 Promise.resolve 参数是一个 promise 时,直接返回该值
    return value;
  }
  return doResolve(new this(INTERNAL), value);  //否则调用doResolve函数
}

Promise.reject = reject;
function reject(reason) {    //返回一个promise对象。
  var promise = new this(INTERNAL);
  return doReject(promise, reason);  //调用doReject
}


Promise.all = all;
function all(iterable) {
  var self = this;
  if (!isArray(iterable)) {
    return this.reject(new TypeError('must be an array'));
  }

  var len = iterable.length;
  var called = false;
  if (!len) {
    return this.resolve([]);
  }

  var values = new Array(len);
  var resolved = 0;
  var i = -1;
  var promise = new this(INTERNAL);

  while (++i < len) {
    allResolver(iterable[i], i);
  }
  return promise;
  function allResolver(value, i) {
    self.resolve(value).then(resolveFromAll, function (error) {
      if (!called) {
        called = true;
        doReject(promise, error);
      }
    });
    function resolveFromAll(outValue) {
      values[i] = outValue;
      if (++resolved === len && !called) {
        called = true;
        doResolve(promise, values);
      }
    }
  }
}

Promise.race = race;
function race(iterable) {
  var self = this;
  if (!isArray(iterable)) {
    return this.reject(new TypeError('must be an array'));
  }

  var len = iterable.length;
  var called = false;
  if (!len) {
    return this.resolve([]);
  }

  var i = -1;
  var promise = new this(INTERNAL);

  while (++i < len) {
    resolver(iterable[i]);
  }
  return promise;
  function resolver(value) {
    self.resolve(value).then(function (response) {
      if (!called) {
        called = true;
        doResolve(promise, response);
      }
    }, function (error) {
      if (!called) {
        called = true;
        doReject(promise, error);
      }
    });
  }
}

37、使用Promises 而非回调(callbacks)优缺点是什么?  

Promise的定义

● Promise,简单来说是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果

● 从语法上说,Promise是一个对象,从它可以获取异步操作的消息

● Promise提供统一的API,各种异步操作都可以用同样的方法进行处理

Promise的特点

a)对象的状态不受外界影响

Promise对象有3种状态,每个promise对象只能是3种状态的一种

1)pending: 表示一个初始状态, 非 fulfilled 或 rejected

2)fulfilled: 成功的操作

3)rejected: 失败的操作

b)状态一旦改变,就不会再发生变化

状态的改变是单向的,只能由pending  ->  fulfilled             pending  ->  rejected,只要这两种情况发生状态就凝固了,这时称为resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果

Promise的优点

●  Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算,是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象

● 有了Promise对象,就可以将异步操作同步操作流程表达出来,避免了层层嵌套的回调函数,简化代码,使代码清晰明了

● 此外,Promise对象提供统一的接口,使得控制异步操作更加容易

Promise的缺点

● 无法取消Promise,一旦新建它,就会立即执行,无法中途取消
● 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
● 当处于Pending状态时,无法得知目前进展到哪一个阶段刚刚开始还是即将完成

Promise的基本用法

Promise对象是一个构造函数,用来生成Promise实例

// resolve--状态从“未完成”变为“成功”(即从 pending 变为 resolved),
// 在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
// reject--状态从“未完成”变为“失败”(即从 pending 变为 rejected),
// 在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
// Promise 新建后就会立即执行
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

// Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
// 分别为resolve(param1)-必写,reject(param2)--可选
// param1--可以是普通变量也可以是Promise对象
// then(resolve(param1),reject(param2))
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

宏任务macrotask和微任务microtask----------异步任务的两种分类

在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

  • macro-task: script(整体代码), setTimeoutsetIntervalsetImmediate, I/O, UI rendering
  • micro-task: process.nextTickPromises(这里指浏览器实现的原生 Promise), Object.observeMutationObserver

//由于在macrotask中先执行了整个js文件,然后去遍历microtask里面的事件
setTimeout(function () {  //-----macrotask
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function () {  //-----microtask
  console.log('promise1');
}).then(function () {
  console.log('promise2');
});


// 输出结果的顺序
promise1
promise2
setTimeout

同一个promise对象可以多次调用then方法


var promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('haha')
    console.log('1')
  }, 1000)
})
promise.then(() => {    //当promise的状态发生改变的时候,才会触发then中的参数函数,所以先输出'3'
  promise.then(() => {  //同一个promise对象可以多次调用then方法
    console.log('3')
  })
  console.log('2')
})

//  1    2    3
setTimeout(function () {
  console.log('three');  //在下一轮“事件循环”开始时执行
}, 0);

Promise.resolve().then(function () {
  console.log('two');   //在本轮“事件循环”结束时执行
});

console.log('one');     //立即执行代码

//执行结果  one     two   three

Promise模拟终止

构造函数Promise(fn)中的参数fn(resolve,reject),当初始化Promise对象实例的时候没有传进来resolve参数,那么这个promise对象的状态就是pending状态,此时原Promise链将会终止执行

Promise对象的几种API---race()  all()   resolve()   reject()是静态方法

1)Promise.prototype.then():promise实例具有then方法,它的作用是为promise实例添加状态改变时的回调函数

then()方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。then方法返回的是一个新的promise实例,非原来的那个promise实例,因此可以采用链式写法,then方法之后还可调用一个then方法。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。then方法可以被同一个promise对象调用多次

var p=new Promise(function(resolve,eject){
    resolve("ok");    
});
p.then(function(value){console.log(val)},
 function(err)(console.log(err))       
);
getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

2)Promise.prototype.catch():这个方法是Promise.prototype.then(null,rejection)的别名,专门只能用来捕获错误信息,用于指定发生错误时的回调函数,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。

Promise放在try...catch里面的结果是什么

Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止,即错误总是会被下一个catch语句捕获

getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});

Promise 在resolve语句后面,再抛出错误,错误不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了;Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');
});
promise.then(function(value) { console.log(value) }).catch(function(error) { console.log(error) });
// ok

3)Promise.resolve():将现有的对象转为Promise对象,状态是resolved,进而可以执行这些方法

参数种类:

a) 参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例

b) 参数是一个thenable对象,将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法,状态为resolved

c) 参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved

d) Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象

4)Promise.reject():返回一个新的 Promise 实例,状态为rejected,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数

5)Promise.all():将多个promise实例,包装成一个新的promise实例,用于一次性处理多个请求

// 参数为数组,数组里面每个元素都是Promise实例
// p1、p2、p3都是 Promise 实例
// 参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
// p的状态由p1、p2、p3决定:只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled
// 此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
// 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返
// 回值,会传递给p的回调函数
const p = Promise.all([p1, p2, p3]);

6)Promise.race():将多个promise实例,包装成一个新的promise实例

// 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
// 那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
const p = Promise.race([p1, p2, p3]);

详细介绍的链接:https://www.cnblogs.com/huangzhilong/p/5358493.html

37、Promise中的链式操作实现原理,JQuery中链式操作实现原理

jQuery中的链式操作实现原理大致思路:

1)就是在jQuery对象上的方法的最后面返回this对象,由于js中的this对象的值是由函数的运行环境决定的,当调用方法的时候最后面返回this对象,这个this对象就指向方法前面的对象,从而可以继续调用该对象中的方法,从而实现链式操作

2)Promise中的链式操作实现原理

在方法中返回一个新的Promise对象

通过Promise.prototype.then方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态
转变和通知观察者。

37、使用Promises实现ajax请求(函数返回值是一个Promise对象)

思路:定义一个函数,在函数里面new Promise,并将其作为函数的返回值。Promise对象的唯一参数是一个函数fn,fn函数有两个参数分别是resolve,reject,在函数fn里面实现ajax,当请求成功调用resolve()将请求到的数据返回,当请求失败,调用reject(),将错误抛出。

用jQuery封装的ajax()方法实现ajax:

1)定义一个函数,在函数里面new Promise,并将其作为函数的返回值

2)Promise对象的唯一参数是一个函数fn,fn函数有两个参数分别是resolve,reject

3)在函数fn里面调用$.ajax()方法

4)当请求成功调用resolve(data)将请求到的数据返回;当请求失败,调用reject(data),将错误抛出

// (jquery)封装Promise对象和ajax过程
var jqGetAjaxPromise = function(param){
    return new Promise(function(resolve, reject){
        $.ajax({
            url: param.url,
            type: 'get',
            data: param.data || '',
            success: function(data){
                resolve(data);
            },
            error: function(error){
                reject(error)
            }
        });
    });
};
// 调用示例
var p2 = jqGetAjaxPromise({    
    url: 'cross-domain1.txt'
});
p2.then(function(data){      
    console.log(data);
    return jqGetAjaxPromise({  
        url:'cross-domain2.txt'
    });
}).then(function(data2){   
    console.log(data2);
}).catch(function(err){
    console.log(err);
});

用原生的js实现ajax,用Promise实现ajax

// 使用get实现ajax
function PromiseAjax (url) {
    return new Promise( (resolve, reject) => {
        // new一个XMLHTTPRequest对象
        var xhr;
        if(window.XMLHttpRequest){
            xhr = new XMLHttpRequest();
        }else{
            xhr = new ActiveXObjext('Microsoft.XMLHTTP');
        }
        
        // 建立连接
        xhr.open('GET', url, true)
        // 发送请求
        xhr.send()

        // 监听状态变化
        xhr.onreadystatechange = function () {
            if (this.readyState === 4) {
                if (this.status === 200) {
                    resolve(this.responseText)
                } else {
                    var resJson = { code: this.status, response: this.statusText }
                    reject(resJson);
                }
            }
        }

    })

}

// 使用Post实现ajax
function postJSON(url, data) {
    return new Promise( (resolve, reject) => {
        // new一个XMLHTTPRequest对象
        var xhr;
        if(window.XMLHttpRequest){
            xhr = new XMLHttpRequest();
        }else{
            xhr = new ActiveXObjext('Microsoft.XMLHTTP');
        }

        // 建立连接
        xhr.open("POST", url, true)
        // 设置请求头,内容类型
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        // 发送请求
        xhr.send(JSON.stringify(data))
        // 监听状态变化
        xhr.onreadystatechange = function () {
            if (this.readyState === 4) {
                if (this.status === 200) {
                    resolve(JSON.parse(this.responseText), this)
                } else {
                    var resJson = { code: this.status, response: this.statusText }
                    reject(resJson, this)
                }
            }
        }

    })
}

Vue的diff实现原理????

Vue通过以下措施来提升diff的性能

(一)优化处理特殊场景

(1)、头部的同类型节点、尾部的同类型节点

这类节点更新前后位置没有发生变化,所以不用移动它们对应的DOM

(2)、头尾/尾头的同类型节点

这类节点位置很明确,不需要再花心思查找,直接移动DOM就好

处理了这些场景之后,一方面一些不需要做移动的DOM得到快速处理,另一方面待处理节点变少,缩小了后续操作的处理范围,性能也得到提升

(二)、原地复用

“原地复用”是指Vue会尽可能复用DOM,尽可能不发生DOM的移动。Vue在判断更新前后指针是否指向同一个节点,其实不要求它们真实引用同一个DOM节点,实际上它仅判断指向的是否是同类节点(比如2个不同的div,在DOM上它们是不一样的,但是它们属于同类节点),如果是同类节点,那么Vue会直接复用DOM,这样的好处是不需要移动DOM。再看上面的实例,假如10个节点都是div,那么整个diff过程中就没有移动DOM的操作了。

下载文档渲染页面的过程中

1)浏览器会通过解析HTML文档构建DOM树

2)解析CSS产生CSS render tree(不包括位置和大小属性)

3)javascript在代码解析的过程中,可能会修改生成的dom树、css render tree,之后根据dom树和css  render tree构建构建树(包括位置和大小属性),在这个过程中css会根据选择器匹配HTML元素。渲染树包括了每个元素的大小,边距等样式属性

渲染树不包含隐藏元素及head元素不可见元素。最后浏览器根据元素的坐标和大小来计算每个元素的位置,并绘制这些元素到页面上

虚拟DOM的定义(VDOM的定义)

1)vdom树状结构,其节点为VNode

2)vnode浏览器DOM中的Node一一对应,通过vnode的elm属性可以访问对应的Node

3)vdom因为是纯粹的JS对象,所以操作它会很高效,但是vdom的变更最终会转换成DOM操作,为了实现高效的DOM操作,Vue中采用了diff算法,其是基于snabbdom改造过来的


 

Vue中的Virtual  DOM

1) Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多

2)在 Vue.js 中,Virtual DOM 是用 VNode 这么一个 Class 去描述

3)实际上 Vue.js 中 Virtual DOM 是借鉴了一个开源库 snabbdom 的实现,然后加入了一些 Vue.js 特色的东西

4)vue中的VNode 是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等其它属性都是都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。由于 VNode 只是用来映射到真实 DOM 的渲染不需要包含操作 DOM 的方法,因此它是非常轻量和简单

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

  // strictly internal
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?
  asyncFactory: Function | void; // async component factory function
  asyncMeta: Object | void;
  isAsyncPlaceholder: boolean;
  ssrContext: Object | void;
  fnContext: Component | void; // real context vm for functional nodes
  fnOptions: ?ComponentOptions; // for SSR caching
  fnScopeId: ?string; // functional scope id support

  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions,
    asyncFactory?: Function
  ) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
    this.ns = undefined
    this.context = context
    this.fnContext = undefined
    this.fnOptions = undefined
    this.fnScopeId = undefined
    this.key = data && data.key
    this.componentOptions = componentOptions
    this.componentInstance = undefined
    this.parent = undefined
    this.raw = false
    this.isStatic = false
    this.isRootInsert = true
    this.isComment = false
    this.isCloned = false
    this.isOnce = false
    this.asyncFactory = asyncFactory
    this.asyncMeta = undefined
    this.isAsyncPlaceholder = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child (): Component | void {
    return this.componentInstance
  }
}

为什么需要虚拟DOM(VDOM)

当用传统的源生api或jQuery去操作DOM时,浏览器会从构建DOM树从头到尾执行一遍流程。比如当你在一次操作时,需要更新10个DOM节点,理想状态是一次性构建完DOM树再执行后续操作。但浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程。显然前面做的都是无用功,只有最后一次有效。同时浏览器的标准把DOM设计的很复杂,所以频繁操作DOM的代价很高,会出现页面卡顿,影响用户体验

VDOM就是为了解决这个浏览器性能问题而被设计出来的。Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。例如前面的例子,假如一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,通知浏览器去执行绘制工作,这样可以避免大量的无谓的计算量

js对象模拟DOM节点的好处是,页面的更新可以先全部反映js对象上,操作内存中的js对象的速度显然要快多了。等更新完后,再将最终的js对象映射成真实的DOM,交由浏览器去绘制

vue中的computed的实现原理---------需要建立数据依赖搜集,动态计算实现原理

1)问题:计算属性如何与属性建立依赖关系?属性发生变化又如何通知到计算属性重新计算?

如何建立依赖关系?----------利用 JavaScript 单线程原理和 Vue 的 Getter 设计,通过一个简单的发布订阅,就可以在一次计算属性求值的过程中收集到相关依赖

2)data 属性初始化 getter setter:通过Object.defineProperty()给data设置setter  getter

// src/observer/index.js
 
// 这里开始转换 data 的 getter setter,原始值已存入到 __ob__ 属性中
Object.defineProperty(obj, key, {
 enumerable: true,
 configurable: true,
 get: function reactiveGetter () {
  const value = getter ? getter.call(obj) : val
  // 判断是否处于依赖收集状态
  if (Dep.target) {
   // 建立依赖关系
   dep.depend()
   ...
  }
  return value
 },
 set: function reactiveSetter (newVal) {
  ...
  // 依赖发生变化,通知到计算属性重新计算
  dep.notify()
 }
})

3)computed属性初始化

// src/core/instance/state.js
 
// 初始化计算属性
function initComputed (vm: Component, computed: Object) {
 ...
 // 遍历 computed 计算属性
 for (const key in computed) {
  ...
  // 创建 Watcher 实例
  // create internal watcher for the computed property.
  watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions)
 
  // 创建属性 vm.reversedMessage,并将提供的函数将用作属性 vm.reversedMessage 的 getter,
  // 最终 computed 与 data 会一起混合到 vm 下,所以当 computed 与 data 存在重名属性时会抛出警告
  defineComputed(vm, key, userDef)
  ...
 }
}
 
export function defineComputed (target: any, key: string, userDef: Object | Function) {
 ...
 // 创建 get set 方法
 sharedPropertyDefinition.get = createComputedGetter(key)
 sharedPropertyDefinition.set = noop
 ...
 // 创建属性 vm.reversedMessage,并初始化 getter setter
 Object.defineProperty(target, key, sharedPropertyDefinition)
}
 
function createComputedGetter (key) {
 return function computedGetter () {
  const watcher = this._computedWatchers && this._computedWatchers[key]
  if (watcher) {
   if (watcher.dirty) {
    // watcher 暴露 evaluate 方法用于取值操作
    watcher.evaluate()
   }
   // 同第1步,判断是否处于依赖收集状态
   if (Dep.target) {
    watcher.depend()
   }
   return watcher.value
  }
 }
}

4)无论是属性还是计算属性,都会生成一个对应的 watcher 实例

5)Dep 的代码短小精悍,但却承担着非常重要的依赖收集环节

用js实现原生的Promise对象????(Promise实现原理)

Vue源码,JQuery源码

38、请使用任意一种mvvm框架演示如何实现双向绑定 angualr     react  vue

vue实现双向绑定的原理     https://blog.csdn.net/tangxiujiang/article/details/79594860

vue中组件:  点击打开链接    https://blog.csdn.net/tangxiujiang/article/details/79620542

vue中的内置组件: 点击打开链接   https://blog.csdn.net/tangxiujiang/article/details/80144195

Vuex的简单介绍:点击打开链接   https://blog.csdn.net/tangxiujiang/article/details/80645416

vue中的混入对象mixins:点击打开链接   https://blog.csdn.net/tangxiujiang/article/details/80644144

Vue开发环境搭建:点击打开链接   https://blog.csdn.net/tangxiujiang/article/details/79802639

vue中的生命周期lifesircle:点击打开链接   https://blog.csdn.net/tangxiujiang/article/details/79608841

vue中的集成指令:点击打开链接   https://blog.csdn.net/tangxiujiang/article/details/79635431

vm.$nextTick()的作用

将回调延迟到下次 DOM 更新循环之后执行。

在修改数据之后,可能数据还没有完全映射到DOM上,这个方法使得更新的数据全部映射到DOM上之后,调用回调函数,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上

38、vue中data、props、computed、methods、watch他们的数据在页面的加载顺序

在生命周期函数里面定义的数据不具有响应式;

data:

props:

computed:computed是在el挂载成功之后执行(也就是HTML DOM加载成功之后马上执行)

methods:有一定的触发条件才执行,如click事件

watch:观察vue实例上的数据变动,然后执行一定的事件

computed   methods    watch的执行顺序

默认加载时:先computed在watch,不执行methods

触发某事件时:先method,在watch

加载顺序

props(对象 [成员是属性]) > methods (对象 [成员是函数] )> data (对象或函数----返回值为对象)> computed(对象 [成员是函数,有返回值] )> watch(对象 [成员是函数,无返回值])

加载时间:在beforeCreate和created之间

作用:

props:接受父组件传递过来的数据

methods:提供相对复杂的数据计算                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  

data:定义以及初始化数据。最好是用于视图上展示的数据,否则最好定义在外面或者vm对象内(减少开支,提高性能);组件内只接受函数

computed:提供相对简单的数据计算(实时更新数据)

watch:当数据发生变化的时候,进行一些操作(提供新值和旧值)(观察数据,有变化执行一些方法)

data与computed的联系

data一般是定义想要在view中显示的数据;computed对数据进行复杂的操作转化(也有可能用在view中)

methods与computed的联系

相同点:可以把同一函数定义为一个方法而不是计算属性,两种方式最后的结果一样的

不同点:计算属性基于他们的依赖进行缓存的,只有相关依赖的值发生改变才会重新求值;而方法只要事件被触发就会再次执行该函数。如果你不希望有缓存,请用方法来代替。

watch与computed的联系

watch:观察某一特定的值,执行特定的函数,有时候会异步调用 API 返回相应的值,computed做不到这一点

computed:依赖变动,实时更新数据

38、Vue中实现window.localStorage的封装(vue中实现window.sessionStorage的封装)

点击打开链接     https://blog.csdn.net/tangxiujiang/article/details/80237914

38、Vue组件的通信方式----实现页面组件之间的通信

一、父子组件通信

1)父组件向子组件传递数据

在父组件的<template>中的标签通过v-bind绑定一个数据属性,然后在子组件的props选项里面定义该属性

// 子组件food.vue
<food @add="addFood" :food="selectedFood" ref="food"></food>
props: {
  food: {
    type: Object
  }
}


// 父组件goods.vue
<food @add="addFood" :food="selectedFood" ref="food"></food>
data() {
  return {
    selectedFood: {}
  };
}


2)子组件向父组件传递数据

在子组件里面通过$emit()函数向父组件传递数据,然后在父组件的页面引用子组件的标签通过v-on指令绑定与子组件同名的事件名

// 子组件food.vue
<cartcontrol @add="addFood" :food="food"></cartcontrol>


methods:{
  addFood(target) {
    this.$emit('add', target);
  }
}


// 父组件goods.vue
<food @add="addFood" :food="selectedFood" ref="food"></food>
methods:{
  addFood(target) {
    this._drop(target);
  }
}


二、非父子组件通信方式----传递数据

1)、可以用Vue.$emit自定义事件来解决----Vue的eventBus事件

// 1、创建 EventBus
//新建一个 js 文件,写下如下代码就创建好了一个 eventbus
import Vue from 'vue'
export default new Vue;

// 在 main.js 中导入 eventbus ,然后将它挂载到 vue 的原型上,这样就可以全局调用了
import bus from './utils/eventBus'
Vue.prototype.bus = bus;

// 2、发送事件,在触发事件的地方发送事件
// $emit(),里面需要一个string 类型的事件名,我这里是用的当前路由的 path 作为事件名
this.bus.$emit(this.$route.path);

// 3、接收事件,事件已经发送,接下来只需要在需要接收事件的地方接
// 收这个事件,然后对事件进行响应就可以了
this.bus.$on(this.$route.path,()=>{
    this.getData();   //接收事件的时候同样需要一个事件名,
                      //然后是一个函数来进行事件响应,我这里是调用了下获取数据的接口
});
var bus = new Vue()   //新建eventBus
// 组件A,发送事件
bus.$emit('id-selected', 1)

// 组件B接收事件,对事件进行响应
bus.$on('id-selected', function (id) {
 console.log(id)
})

问题:重复触发事件问题

a)事件会重复触发,而且每次切换过路由后事件执行次数就会加一,假如用户非常频繁的切换页面,那事件执行次数会越来越多

原因事件是全局的,它并不会随着组件的销毁而自动注销需要手动调用注销方法来注销

解决方法:在组件的 beforeDestroy ,或 destroy 生命周期中执行注销方法手动注销事件

beforeDestroy() {
    //组件销毁前需要解绑事件,否则会出现重复触发事件的问题
    this.bus.$off(this.$route.path);
}

https://www.jianshu.com/p/fde85549e3b0   例子1

https://blog.csdn.net/zgh0711/article/details/80284830    例子2

2)使用Vuex

1)vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

2)把组件的共享状态抽取出来,以一个全局单例模式管理

3)通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,代码变得更结构化、易维护

4)Vuex是专门用来解决多个视图依赖于同一状态,来自不同视图的行为需要变更同一状态的问题

vuex有五个核心概念,其中state和getters主要是用于数据的存储与输出,而mutations和actions是用于提交事件并修改state中的数据,module模块数据按照组件之间的功能联系来分离,并把它们拆分到一个个小文件中,避免了vuex文件太大 

// 这是vuex总的管理文件,将各模块统一在一起,从而将每一个分支都连接到vuex这个总的状态树上
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 引入应用状态管理
import goodshandleStore from './goodshandle'
import orderStore from './order'
import addressStore from './address'
import shopcarStore from './shopcar'
import merchantStore from './merchant'
import couponStore from './coupon'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    handle: goodshandleStore,
    order: orderStore,
    address: addressStore,
    shopcar: shopcarStore,
    merchant: merchantStore,
    coupon: couponStore
  }
})

vue中的computed选项的原理:详情    https://blog.csdn.net/tangxiujiang/article/details/81170490

vue-router路由懒加载-----懒加载也叫作延迟加载,在需要的时候在加载

为什么需要懒加载?

像vue这种单页面应用,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了loading也是不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时

简单的说就是:进入首页,不用一次加载过多资源,造成用时过长

路由懒加载----就是在创建路由的时候在component属性上是一个函数,然后函数返回通过require引进来的组件

路由非懒加载:在页面顶部把路由用到的所有组件引进来,然后在创建路由的时候component属性就是引进来的组件名

问题:用了懒加载后打完包直接运行那个index.html会报错,报文件引用错误其实是打包时候路径配置有点问题修改下就好了

找到build下面的webpack.prod.conf.js   添加   publicPath:"./"

什么是vue-loader?

vue-router、vue-loader、vue-cli,vue-source,vuex的解析:打开链接

 https://blog.csdn.net/tangxiujiang/article/details/76582955

实现调用delayHello之后,隔1秒打印5

var hello = function(a){
    console.log(a);
  };
  
  function delay(func,t){
    return function(n){
        setTimeout(function(){
            func(n);
        },t);
    }
  }
  
  var delayHello = delay(hello,1000);
  delayHello(5);

loader完之后,WebPack输出什么?

本质上来说,loader 就是一个 node 模块,在 webpack 的定义中,loader 导出一个函数,loader 会在转换源模块(resource)的时候调用该函数。在这个函数内部,我们可以通过传入this上下文给 Loader API 来使用它们, loader 的功能:把源模块转换成通用模块

webpack中的loader,plugin是什么?
答:loader是用来告诉webpack如何转化处理某一类文件,并且引入到打包出的文件中,loader一定对应的是文件

plugin是用来自定义webpack打包过程中的方式,一个插件是含有apply方法的一个对象,通过这个方法,可以参与到整个

webpack打包的各个流程(生命周期)

loader的作用------处理源文件,一次处理一个

1、实现对不同格式文件的处理,比如说将scss/styl/less转换为css,或者typescript转化为js
2、转换这些文件,从而使其能够被添加到依赖图中

loader是webpack最重要的部分之一,通过使用不同的Loader,我们能够调用外部的脚本或者工具,实现对不同格式文件的处理loader需要在webpack.config.js里边单独用module进行配置

webpack中几个常用的loader:
babel-loader: 让下一代的js文件转换成现代浏览器能够支持的JS文件

css-loader,style-loader:两个建议配合使用,用来解析css文件,能够解释@import,url()

less-loader:解析.less文件

file-loader: 生成的文件名就是文件内容的MD5哈希值,并会保留所引用资源的源

url-loader: 功能类似 file-loader,但是文件大小低于指定的限制时,可以返回一个DataURL事实上,在使用less,scss,stylus这些的时候,npm会提示你差什么插件,差什么,你就安上就行了

babel的工作原理(babel将ES6转成ES5之类的原理)---babel就是一个编译器

1)解析:将代码字符串解析成抽象语法树

2)变换:对抽象语法树进行变换操作

3)在建:在根据变换后的抽象语法树,生成字符串代码

webpack配置问题------如何自动生成webpack配置

webpack-cli,vue-cli等

webpack开发问题------webpack-dev-server和http服务器如nginx有什么区别?

webpack-dev-server使用内存存储webpack开发环境下的打包文件,并且可以使用模块热更新,他比传统的http服务对开发更加有效

webpack-dev-server就是express+hot**组合,增加一些配置而成的

什么是webpack热更新?

1)模块热更新,是webpack的一个功能,他可以使得代码修改后,不用刷新浏览器,页面就可以更新

2)webpack-hot-middleware中间件是webpack的一个plugin,通常结合webpack-dev-middleware一起使用。借助它可以实现浏览器的无刷新更新(热更新),即webpack里的HMR(Hot Module Replacement)

webpack热更新原理

使用层面

1)webpack加入webpack-hot-middleware后,内存中的页面将包含HMR相关js,加载页面后,Network栏可以看到有__webpack_hmr请求,如下图:

2)__webpack_hmr是一个type为eventsource的请求, 从Time栏可以看出:默认情况下,服务器每十秒推送一条信息到浏览器

3)如果此时关闭开发服务器,浏览器由于重连机制,将持续抛出类似GET http://www.test.com/__webpack_hmr 502 (Bad Gateway) 这样的错误。重新启动开发服务器后,重连将会成功,此时便会刷新页面

使用EventSource技术实现热更新

1)SSE(Server-Sent  Event,服务器发送事件)是围绕只读Comet交互推出的API或者模式

2)SSE API用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据

3)服务器响应的MIME类型必须是text/event-stream,而且是浏览器中的javascript API能解析格式输出

4)SSE支持短轮询、长轮询、HTTP流,能在断开连接时自动确定何时重新连接

需要一个高效的节省资源的方式去获取服务器信息,一旦服务器资源有更新,能够及时地通知到客户端,从而实时地反馈到用户界面上,SSE API中的EventSource对象就是这样的技术,它本质上还是HTTP,通过response流实时推送服务器信息到客户端

var source = new EventSource('event.php')

注意:传进的url须与创建对象的页面同源

服务端实现event.php接口,需要返回类型为 text/event-stream的响应头

var http = require('http');
http.createServer(function(req,res){
  if(req.url === 'event.php'){
    res.writeHead(200,{
      'Content-Type': 'text/event-stream',  //MIME是text/event-stream
      'Cache-Control': 'no-cache',    //避免缓存
      'Connection': 'keep-alive'      //能够发送多个response
    });
    setInterval(function(){
      res.write('data: ' + +new Date() + '\n');  //发送数据时,务必保证服务器推送的数据以 data:开始,以\n结束
    }, 1000);                                      //这是约定俗成的
  }
}).listen(8888);

服务器每隔1s主动向客户端发送当前时间戳,为了接受这个信息,客户端需要监听服务器

es.onmessage = function(e){
      console.log(e.data); // 打印服务器推送的信息
}

猜你喜欢

转载自blog.csdn.net/tangxiujiang/article/details/88718590
今日推荐