npm scripts 使用指南

Node 开发离不开 npm,而脚本功能是 npm 最强大、最常用的功能之一。

本文介绍如何使用 npm 脚本(npm scripts)。

一、什么是 npm 脚本?

npm 允许在package.json文件里面,使用scripts字段定义脚本命令。

{ // ...
  "scripts":{"build":"node build.js"}}
  

上面代码是package.json文件的一个片段,里面的scripts字段是一个对象。它的每一个属性,对应一段脚本。比如,build命令对应的脚本是node build.js

命令行下使用npm run命令,就可以执行这段脚本。

$ npm run build
  # 等同于执行
  $ node build.js
  

继续阅读npm scripts 使用指南

仿原生高性能下拉刷新上拉加载组件

前言

曾经第一版的下拉刷新组件是用scoll5开发,说实话很愁,因为不光在安卓有很大的资源开销,很卡,ios也不是很近人意,尤其是列表中还有一些input什么的,后来下定决心不用了,用一些更原生的方法去写

组件说明

组件下拉是通过触摸位置来控制顶部div高度变化来模拟,中间的滚动是采用浏览器自然滚动,需要在body上加入css样式-webkit-overflow-scrolling: touch,使滚动更加顺滑,上拉加载动作则是通过滚动位置来判断是否到了底部,到了则去执行相应的代码,这样整个滚动的性能更加好!
如果有更好的方法请不吝赐教,让我们的组件更加完善

百说不如一个 demo

使用方法奉上

//下拉加载更多
upDown.up({
    container: $("body"),
    callback: function (me) {
        //下拉后回调
        setTimeout(function () {
            //事例3秒后恢复,下拉后记得清空下列表,恢复到第一页加载到状态
            num=0;
            lastPage=false;
            me.pullToRefreshDone();//此方法是结束下拉转圈恢复
        }, 3000);
    }
});
//测试代码 定义个模版变量,等页面滚动到底部的时候把此模版加载到页面底部
var tpl="",num= 0,lastPage=false;
for(var i=0;i<10;i++){
    tpl=tpl+'<li class=" proItem" ><a>我是list </a></li>';
};
//上拉加载更多
upDown.down(function(fun){
    //上拉后回调
    $(".btmDIv").show();
    if(num<3){
        setTimeout(function () {
            //事例3秒后恢复
            $(".listBox").append(tpl);
            fun.reset(lastPage);//当为true的时候 为最后一页了
            num=num+1;
        }, 2000);
    }else {
        lastPage=true;
        fun.reset(lastPage);//当为true的时候 为最后一页了
    }
})

如果用的好请打个赏把!

彻底深刻理解js之this

前言

本来是要写一篇关于js的this的,但是发现已经有人写的很好了,我就觉得没必要再写了,直接拿来.欢迎去原作地址查看:http://www.jianshu.com/p/d647aa6d1ae6

我们在学习JavaScript的过程中,由于对一些概念理解得不是很清楚,但是又想要通过一些方式把它记下来,于是就很容易草率的给这些概念定下一些方便自己记忆的有偏差的结论。

危害比较大的是,有的不准确的结论在网上还广为流传。
继续阅读彻底深刻理解js之this

pdf.js 异步获取pdf流文件显示,支持移动端,微信

简单说一说碰到的坑

公司最近在有一个需求就是pdf要现在预览,所以就找到了pdf.js的插件,开始用了它的viewr.js,这个是美化了预览页面,但是在移动端safari会出问题,所以只能放弃,自己写喽

下面贴上代码,注释写清楚了就不一一解释了,希望我走的坑你不要再趟一次

** 先看一下上线版本地址,可以移动端打开查看 **

var param = function(name) { //一个获取url中参数的方法
    var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
    var r = window.location.search.substr(1).match(reg); //匹配目标参数
    if (r != null) return unescape(r[2]);
    return null; //返回参数值
};
var PDFData = "";
var currPage = 1; //当前页数从1开始
var numPages = 0;
var thePDF = null;
$.ajax({
    type: "post",
    mimeType: 'text/plain; charset=x-user-defined',
    url: param("pdfUrl"),
    //url中要有 pdfUrl 参数和值,值为pdf流地址
    success: function(data) {
        if (data) {
            PDFData = data;
            var rawLength = PDFData.length;
            //转换成pdf.js能直接解析的Uint8Array类型,见pdf.js-4068
            var array = new Uint8Array(new ArrayBuffer(rawLength));
            for (i = 0; i < rawLength; i++) {
                array[i] = PDFData.charCodeAt(i) & 0xff;
            }
            PDFJS.getDocument(array).then(function(pdf) {
                require('module/common/dialog').hideLoading();
                //将pdf对象赋值到全局变量,能够在其他方法中使用
                thePDF = pdf;
                //获取一共有多少页
                numPages = pdf.numPages;
                //从第一页开始
                pdf.getPage(1).then(handlePages);
            });
        } else {
            console.log("pdf请求失败")
        }
    }
});
function handlePages(page) {
    //获取全尺寸pdf
    var viewport = page.getViewport(2);
    var canvas = document.createElement("canvas");
    var canvasCon = document.createElement("div");
    canvas.id = "canvas_" + currPage;
    canvasCon.id = "canvasCon_" + currPage;
    canvasCon.className = "canvasCon";
    var winRatio = ($(window).width() / viewport.width) * 0.9;
    $(canvas).css({
        "transform": "scale(" + winRatio + ")",
        "webkitTransform": "scale(" + winRatio + ")",
        "left": $(window).width() * 0.05
    });
    $(".canvasCon").css({
        'height': viewport.height * winRatio
    });
    var context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;
    //在canvas上绘制
    page.render({
        canvasContext: context,
        viewport: viewport
    });
    //在页面中插入画布
    document.body.appendChild(canvasCon);
    document.getElementById("canvasCon_" + currPage).appendChild(canvas);
    //开始下一页到绘制
    currPage++;
    if (thePDF !== null && currPage <= numPages) {
        thePDF.getPage(currPage).then(handlePages);
    }
}

 

经过一段时间,发现大家很多连基本的pdf.js的用法都不太了解,这里放出一个例子

http://0313.name/demo/pdfjs_helloworld/helloworld.html

拿去不谢

如有问题,下面有赞赏码,有求必应~

javascript魔法-Object.defineProperty

前几天在看一些流行的迷你mvvm框架(比如avalon.js、 vue.js 这种较轻的框架,而非Angularjs、Emberjs这种较重的框架)的实现。现代流行的mvvm框架一般都会将数据双向绑定(two-ways data binding)做掉,作为框架自身的一个卖点( Ember.js 貌似是不支持数据双向绑定的。),而且每种框架双向数据绑定的实现方式都不太一致,比如Anguarjs内部使用的是 脏检查 ,而avalon.js内部实现方式的本质是设置 属性访问器 。

双向数据绑定的常规实现方式

首先我们来说一下何为前端的 双向数据绑定 。简单的来说,就是框架的控制器层(这里的控制器层是一个泛指,可以理解为控制view行为和联系model层的中间件)和UI展示层(view层)建立一个双向的数据通道。当这两层中的任何一方发生变化时,另一层将会立即(或者看起来是 立即 )自动作出相应的变化。

一般来说要实现这种双向数据绑定关系(控制器层与展示层的关联过程),在前端目前会有三种方式,

脏检查
观察机制
封装属性访问器
脏检查

我们说Angularjs(这里特指AngularJS 1.x.x版本,不代表AngularJS 2.x.x版本)双向数据绑定的技术实现是脏检查,大致的原理就是,Angularjs内部会维护一个序列,将所有需要监控的属性放在这个序列中,当发生某些特定事件时(注意,这里并不是定时的而是由某些特殊事件触发的),Angularjs会调用 $digest 方法,这个方法内部做的逻辑就是遍历所有的watcher,对被监控的属性做对比,对比其在方法调用前后属性值有没有发生变化,如果发生变化,则调用对应的handler。网上有许多剖析Angularjs双向数据绑定实现原理的文章,比如 这篇 ,再比如 这篇 ,等等。

这种方式的缺点很明显,遍历轮训watcher是非常消耗性能的,特别是当单页的监控数量达到一个数量级的时候。

Object.defineProperty

国产mvvm框架avalon.js实现数据双向绑定的原理就是属性访问器。不过它当然不会像上述示例代码一样原始。它使用了ECMAScript5.1(ECMA-262)中定义的标准属性 Object.defineProperty 方法。针对国内行情,部分还不支持 Object.defineProperty 低级浏览器采用VBScript作了完美兼容,不像其他的mvvm框架已经逐渐放弃对低端浏览器的支持。

我们先来MDN上对 Object.defineProperty 方法的定义,

The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object.

意义很明确, Object.defineProperty 方法提供了一种直接的方式来定义对象属性或者修改已有对象属性。其方法原型如下,

Object.defineProperty(obj, prop, descriptor)

其中,

obj ,待修改的对象
prop ,带修改的属性名称
descriptor ,待修改属性的相关描述
descriptor 要求传入一个对象,其默认值如下,

{
    configurable: false,
    enumerable: false,
    writable: false,
    value: null,
    set: undefined,
    get: undefined
}

1、configurable ,属性是否可配置。可配置的含义包括:是否可以删除属性( delete ),是否可以修改属性的 writable 、 enumerable 、 configurable 属性。
2、enumerable ,属性是否可枚举。可枚举的含义包括:是否可以通过 for…in 遍历到,是否可以通过 Object.keys() 方法获取属性名称。
3、writable ,属性是否可重写。可重写的含义包括:是否可以对属性进行重新赋值。
4、value ,属性的默认值。
5、set ,属性的重写器(暂且这么叫)。一旦属性被重新赋值,此方法被自动调用。
6、get ,属性的读取器(暂且这么叫)。一旦属性被访问读取,此方法被自动调用。

不过还是有一点需要额外注意一下, Object.defineProperty() 方法设置属性时,属性不能同时声明访问器属性( set 和 get )和 writable 或者 value 属性。 意思就是,某个属性设置了 writable 或者 value 属性,那么这个属性就不能声明 get 和 set 了,反之亦然。

因为 Object.defineProperty() 在声明一个属性时,不允许同一个属性出现两种以上存取访问控制。

示例代码,

var o = {},
    myName = 'erik';

Object.defineProperty(o, 'name', {
    value: myName,
    set: function(name) {
        myName = name;
    },
    get: function() {
        return myName;
    }
});

上面的代码看起来貌似是没有什么问题,但是真正执行时会报错,报错如下,

TypeError: Invalid property.  A property cannot both have accessors and be writable or have a value, #<Object>

因为这里的 name 属性同时声明了 value 特性和 set 及 get 特性,这两者提供了两种对 name 属性的读写控制。这里如果不声明 value 特性,而是声明 writable 特性,结果也是一样的,同样会报错。

彻底深刻理解js原型链之prototype,__proto__以及constructor(二)

前言

如果你能够啃下教程一并且吃透原型链的几个概念的话说明你在前端飞仙的路上又进了一小步···学习最怕的不是慢而是站!这篇教程主要目的对原型链概念进一步加深理解

巩固下教程一的知识

来看下面的例子:

var text=new String("我是文字");
function Persion(name,job){
    this.name=name;
    this.job=job;
}
Persion.myName="lxm";
Persion.prototype.sayName=function(){
    alert(this.name);
}
var perison1=new Persion("lxm","20")

继续阅读彻底深刻理解js原型链之prototype,__proto__以及constructor(二)

彻底深刻理解js原型链之prototype,__proto__以及constructor(一)

前言

以下概念请花费一定的时间彻底理解,才能进行下一步,思考题一定要思考,这样才能彻底掌握原型链的知识点,教程中如果有任何的错误不足请指正!

函数对象

由function创造出来的函数,比如:

    function a(){};
    var b=function(){};

系统内置的函数对象

Function,Object,Array,String,Number

只有函数对象才有 prototype属性 ,重要的事情说三遍!

思考: js的引用数据类型都属于函数对象吗?

普通对象

除开函数对象之外的对象都是普通对象

    var b='qwe'; // b 是字符串类型,属于普通对象
    var c=123;; // c 是数字类型,属于普通对象

继续阅读彻底深刻理解js原型链之prototype,__proto__以及constructor(一)