vue+webpack多页面应用设置

整   理:晋 哲

时   间:2017-04-10

说   明:vue+cli脚手架默认搭建的是单页面应用,在实际项目中需要优化配置生成多页面。

设置步骤:
一、修改src文件结构
图一为脚手架默认生成的src结构
p1

图二为调整后的文件结构,新建module文件夹,将每个页面都对应一个同名的文件夹
p2

比如:login文件夹里的三个文件是由原先的App.vue、main.js、index.html整合到一个文件夹内进行修改,welcome文件夹就是再复制一份修改

二、修改build内的文件配置
主要是调整webpack中html-webpack-plugin插件的默认设置,这是用来将js和html对应起来的插件
调整三处js文件,如下图:
p3

1、webpack.base.conf.js
(1)在头部添加下面两行代码

var glob = require('glob'); // 引入glob工具
var entries = getEntry('./src/module/**/*.js'); // 获得入口js文件

(2)再将entry的配置修改为entries
p6-1

(3)在底部添加getEntry方法,用于匹配路径

function getEntry(globPath) {
  var entries = {},
    basename, tmp, pathname;

  glob.sync(globPath).forEach(function (entry) {
    basename = path.basename(entry, path.extname(entry));
    tmp = entry.split('/').splice(-3);
    pathname = tmp.splice(0, 1) + '/' + basename; // 正确输出js和html的路径
    entries[pathname] = entry;
  });
  return entries;
}

p6-2

2、webpack.dev.conf.js
(1)在头部添加下面两行代码,引入path和glob工具

// 引入path和glob工具
var path = require('path');
var glob = require('glob');

(2)将HtmlWebpackPlugin默认设置注释掉
p7-1

(3)在底部也添加getEntry方法,并添加自定义HtmlWebpackPlugin插件的配置

function getEntry(globPath) {
  var entries = {},
    basename, tmp, pathname;

  glob.sync(globPath).forEach(function(entry) {
    basename = path.basename(entry, path.extname(entry));
    tmp = entry.split('/').splice(-3);
    pathname = tmp.splice(0, 1) + '/' + basename; // 正确输出js和html的路径
    entries[pathname] = entry;
  });
  return entries;
}

// 自定义HtmlWebpackPlugin插件配置
var pages = getEntry('./src/module/**/*.html');
for (var pathname in pages) {
  // 配置生成的html文件,定义路径等
  var conf = {
    filename: pathname + '.html',
    template: pages[pathname], // 模板路径
    minify: { //传递 html-minifier 选项给 minify 输出
      removeComments: true
    },
    inject: 'body', // js插入位置
    chunks: [pathname, "vendor", "manifest"] // 每个html引用的js模块,也可以在这里加上vendor等公用模块
  };
  // 需要生成几个html文件,就配置几个HtmlWebpackPlugin对象
  module.exports.plugins.push(new HtmlWebpackPlugin(conf));
}

p7-2

3、webpack.prod.conf.js
和上一个文件webpack.dev.conf.js中做类似的处理,先注释掉原来的HtmlWebpackPlugin,然后在下面添加函数,通过迭代插入多个HtmlWebpackPlugin。

三、最终生成的文件会打包进对应的文件夹内
对比默认生成文件结构与修改后生成文件
p1-1
QQ截图20170630174703

安装vue-cli脚手架构建项目步骤

整   理:晋 哲

时   间:2017-03-30

说   明:安装脚手架,编译生成最终用于实际项目的压缩文件

1、确认已经安装node环境和npm包管理工具,建议将 npm 的注册表源设置为国内的镜像,可以大幅提升安装速度。

npm install -g cnpm --registry=https://registry.npm.taobao.org

pic1

2、安装 vue
pic2

3、全局安装 vue-cli
pic3

4、创建一个基于 webpack 模板的新项目
pic4

5、安装依赖
$ cd my-project
$ npm install

6、实时运行项目,会自动打开一个浏览器窗口,默认访问localhost:8080
$ npm run dev
pic5
pic6

(2)如果是在测试机249上安装,则默认的localhost:8080访问不了,需要修改build/dev-server.js文件里的uri
pic6_2

最终访问:http://192.168.0.249:8080
pic6_3
pic6_4

7、开发版本用于实际项目中,编译vue文件,在dist文件夹内最终生成压缩版html、js、css。
$ npm run build
pic7
pic8

PHP闭包

闭包的介绍

闭包是词法作用域的体现,一般编程语言有这些特性

  1. 函数是一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
  2. 函数可以嵌套定义,即在一个函数内部可以定义另一个函数。

三个关键点

  1. 函数
  2. 自由变量
  3. 作用域

自由变量

let a = 1
let b = function(){
    console.log(a)
}

在这个例子里函数b因为捕获了外部作用域(环境)中的变量a,因此形成了闭包。 而由于变量a并不属于函数b,所以在概念里被称之为「自由变量」。

注意点:

  1. 在一些语言里,「捕获」这一行为还有一个特点:闭包只会捕获自由变量的引用。
  2. 在PHP中,需要使用use关键词显示调用自由变量

作用域

可以使用外部的「自由变量」,内部变量不能被访问,形成一个相对安全的访问环境

总结

  1. 记住定义当时的外部环境
  2. 封闭外部环境
  3. 延迟执行

基本使用

$func = function () {

};

$func();

$a = '1';
$func = function () use ($a) {

}

$func();

作用域

  1. 闭包拥有独立的作用域,基本等同普通函数
  2. 在方法中使用时,等同于实例的方法,可以直接使用$this(>=5.4)

注意点

$this 的使用

在一次开发中遇到关于闭包的一个问题

class Demo
{
    /**
     *
     */
    public function test()
    {
        $data = $this->isCache('name', function () {
            $this->sayHello();
        });
    }

    /**
     *
     */
    protected function sayHello()
    {
        echo 'Hello, World';
    }

    /**
     * @param $name
     * @param $callback
     * @return array
     */
    public function isCache($name, $callback)
    {
        $callback();
        return array();
    }
}

我希望在一个闭包中调用这个类的一个受保护的方法(这里假设是sayHello),结果在运行的时候,直接出现了语法级别的报错。

查了一些资料发现了问题

  1. 5.3 版本不能直接在闭包中使用$this

PHP的bug,在5.4中得以修复

$that = $this;        
$data = $this->isCache('name', function () use ($that) {
    $that->sayHello(); // 这里只能是public
});

看似这个问题解决了,不过在stackoverflow上一位热心人士的提醒中,这样是会出现一些问题的,因为这样传递$this, 是赋值传递,也就是说,在闭包中调用方法导致实例属性的变化不会反应到闭包外面,这样在一些特殊情况,会导致闭包内外属性不一致,所以这里最好使用引用传递除非你知道你在干什么!

$that = $this;        
$data = $this->isCache('name', function () use (&$that) {
    $that->sayHello(); // 这里只能是public
});

如果在5.4版本下,不但可以直接使用$this,而且作用域等同于实例中的方法

$data = $this->isCache('name', function () {
    $that->sayHello(); // 这里可以是任意限制符
});

Closure类

bind和bindTo的区别

// bind,相当把一个类的加上加上一个方法,并且将该方法转换成闭包
class A {
    private static $sfoo = 1;
    private $ifoo = 2;
}
$cl1 = static function() {
    return A::$sfoo;
};
$cl2 = function() {
    return $this->ifoo;
};

$bcl1 = Closure::bind($cl1, null, 'A');
$bcl2 = Closure::bind($cl2, new A(), 'A');
echo $bcl1(), "\n"; // 1
echo $bcl2(), "\n";// 2

// bindTo,改变指定的$this对象和类作用域

class A {
    function __construct($val) {
        $this->val = $val;
    }
    function getClosure() {
        //returns closure bound to this object and scope
        return function() { return $this->val; };
    }
}

$ob1 = new A(1);
$ob2 = new A(2);

$cl = $ob1->getClosure();
echo $cl(), "\n";
$cl = $cl->bindTo($ob2);
echo $cl(), "\n";


虽然两者看上去像,实际上不是一回事。

Vue开发环境搭建

一、安装虚拟机
1)版本:Vmware 12 Pro版 + Cent-OS 32位
2)密钥:5A02H-AU243-TZJ49-GTC7K-3C61N
3)分区:/、/boot、swap

二、配置网络
1)ifconfig查看windows配置
2)修改文件:vim /etc/sysconfig/network-scripts/ifcfg-eth0
# Xen Virtual Ethernet
DEVICE=eth0
BOOTPROTO=none
ONBOOT=yes
HWADDR=2e:c1:12:61:5f:ea
NETMASK=255.255.255.0
IPADDR=192.168.0.249
GATEWAY=192.168.0.1
TYPE=Ethernet

三、配置远程控制器putty
直接设置ip即可,没有选择生成秘钥

四、安装nodejs
1、安装依赖包:yum install gcc-c++ openssl-devel
2、安装最新版本node.js:
cd /usr/local/src
wget http://nodejs.org/dist/node-latest.tar.gz
tar -zxvf node-latest.tar.gz
cd node-v7.7.0
./configure
make && make install
3、如果需要升级gcc,则升级gcc,不要忘记加软连接
4、解决警告:sudo date -s '01:10:00 2017-03-02'
5、重新执行 ./configure 以及 make && make install

五、vue安装
# 全局安装 vue-cli
$ npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
# 安装依赖,走你
$ cd my-project
$ npm install
$ npm run dev

六、本地(windows)与虚拟机(Cent-OS)交互
1、vim build/dev-server.js
修改localhost为虚拟机IPADDR
2、关闭防火墙
3、在本地浏览器输入IP:8080展现vue界面

关于用户体验的十大原则

用户体验的核心和本质就是研究人在特定场景下的思维模式和行为模式,然后顺应和利用它。
举例:
为什么要把网页的重要内容放到页面的左上部分?因为人的视觉注意力会首先锁定在左上的位置。
为什么按钮平常状态和按下状态一般是不同的?因为用户需要反馈,没有反馈人会产生疑惑。
为什么超市收银台面前会有货物架?因为顾客在排队的时候处于非常无聊的状态,这时候任何商品或信息都会引起他们的注意,是销售某些易耗便携物品的绝佳时机。

  1. 系统可见原则
    保证界面的内容可见、状态可见、变化可见
    12
    内容你要考虑到用户最想看见关注的是什么,收货人信息,支付方式和派送方式,总价以及扣除其它折扣优惠等最终用户需要花的钱,这些是用户最关注的点。

    12
    不管是有网、没网,还是错误、成功、警告、提示等状态都要给用户一个反馈;
    14
    像左划右划,上拉下拉,抽屉效果等动画都需要展现出来给用户一个暗示:这个是可以手势操作的。

  2. 可控性原则
    让用户能对当前情况有很好的了解与掌握,要给用户足够的自由度。

15

  1. 一致性
    用户在同一款产品里接受同一套规范和逻辑。比如说收藏、关注、点赞等,不管是名称还是图标都要形成一个大的规范,更换名词和图标会导致用户的学习成本提高。还有行为逻辑一定要规范。

  2. 防错,防呆原则
    要尽量用足够的提醒和设计,让用户不要混淆、犯错和发呆。
    1
    2

  3. 协助用户记忆原则
    在需要记忆某些信息时,产品功能上要帮助用户记忆。如账号密码的记录,短信的草稿等。
    3
  4. 简约易读原则
    界面足够简单、内容易换

  5. 容错原则
    向用户提醒可能出现的错误,并提供能挽回用户错误的方法。
    8
    9

  6. 帮助和提示
    在任何时候,考虑到用户需要得到的帮助情况予以提示,比如删除东西,添加修改等。
  7. 灵活高效原则
    用户在使用时,要能够方便,高效的完成任务,如注册流程,填写资料、分享文章,书写动态等。
  8. 恢复现场原则
    适应用户的碎片化使用习惯,在各种切换和退出返回时,要有恢复现场的能力。比如在看视频电影的时候,有人找你聊天,你退出回复完,再点击进入还能继续观看你的电影。

Git命令-rebase和merge的区别

 编写: zhangyue
 时间: 2017-02-23
 说明: git的一些命令使用

1 merge和rebase的区别
merge:合并两个分支,根据两个分支的同一个起点,同相同起点开始,把两个分支的修改进行合并,之后生成生成一个commit。
rebase:合并两个分支,抽取出当前的分支的commit, 之后合并目标分支commit,再将置顶抽出的commit重新合并入当前分支的commit

2 两者合并的示意图

merge:
    master: master:1 -- master:2
                                 |
    dev   :     |-- dev:1 --- dev:2 执行git merge master

    dev的log:
    dev   : master:1 --> dev:1 --> dev:2 --> master:2 --> merge commit

rebase:
    master: master:1 -- master:2
                                 |
    dev   :     |-- dev:1 -- dev:2 执行git rebase master

    dev的log:
    dev   : master:1 --> master:2 --> dev:1 --> dev:2

3 在发生冲突时两者的不同处理方式
merge: 有冲突->停止合并->手动解决后->再次merge
rebase:有冲突->暂停操作->手动解决,
git rebase --continue继续(--skip跳过/--abort 停止rebase操作)
4 rebase对commit的操作

git rebase -i HEAD~3 对所做的提交进行修改
    # Commands:
    # p, pick = use commit 不做任何修改
    # r, reword = use commit, but edit the commit message 只修改提交注释信息
    # e, edit = use commit, but stop for amending 
        修改提交的文件,做增补提交
    # s, squash = use commit, but meld into previous commit
        将该条提交合并到上一条提交,提交注释也一并合并
    # f, fixup = like "squash", but discard this commit's log message
        将该条提交合并到上一条提交,废弃该条提交的注释
    # x, exec = run command (the rest of the line) using shell
    # d, drop = remove commit
        删除某次提交

5 其他的一些功能命令

    git cherry-pick  取某一个分支中的一个或几个commit(s)来进行合并
    git stash [pop/apply /drop/save] 将改动存入暂存区[弹出暂存区记录/恢复一个暂存区内记录]
    git blame 文件逐行追溯
    git clean 清除工作区未跟踪文件

移动前端自适应解决方案

  1. 拉钩网 典型的弹性布局:关键元素高宽和位置都不变,只有容器元素在做伸缩变换
    文字流式,控件弹性,图片等比缩放
    0
    拉钩网:https://m.lagou.com/

2.网易 设置根元素html字体大小,使用rem,并用js计算不同分辨率下的html字体大小
使用rem布局结合在html上根据不同分辨率设置不同font-size,通过js计算出来的,所以当分辨率发生变化时,html的font-size就会变。
为了计算方便,取一个100px的font-size为参照,如设计稿的横向分辨率为640px,则body的width:6.4rem; 于是html的font-size=deviceWidth / 6.4。
这个deviceWidth就是viewport设置中的那个deviceWidth, 这个deviceWidth通过document.documentElement.clientWidth就能取到了。所以当页面的dom ready后,做的第一件事情就是:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 6.4 +’px’;
视口要如下设置:

meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"

该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这个应该是大家都想要的效果,如果你不这样的设定的话,那就会使用那个比屏幕宽的默认viewport,也就是说会出现横向滚动条。
网易:http://3g.163.com/touch/all?nav=2&dataversion=A&version=v_standard

3.淘宝 动态设置viewport的scale以及动态计算html的font-size
视口要如下设置:

meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"

动态设置viewport的scale

var scale = 1 / devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

device-width的计算公式为:
设备的物理分辨率/(devicePixelRatio * scale);devicePixelRatio称为设备像素比
动态计算html的font-size

document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';

布局的时候,各元素的css尺寸=设计稿标注尺寸/设计稿横向分辨率/10
font-size可能需要额外的媒介查询,并且font-size不使用rem
淘宝:https://m.taobao.com/?sprefer=sypc00#index

Trait

Trait 是一种类似PHP单继承语言而准备的代码复用机制

目前可在很多框架汇总看到,比如Yii 、Laravel

为什么要用到这个?
php是单继承的语言,而Trait就是为了解决php不能多继承提高代码的复用并且摒弃一下多线程的诟病而产生的。

1.确定版本:

使用的是在线调试工具
限制:php5.4开始可以使用

http://www.shucunwang.com/RunCode/php5.4/
图片1
我们选择5.4的版本

ps:当然选择低版本会报错误
syntax error, unexpected T_STRING

2.先简单实现Trait

a)创建
图片2
ps:Trait不能被实例化
b)调用
图片3打印结果:图片4

当要调用多个的时候
图片5打印结果:图片6

3.优先级(如果类中方法与trait中重名,那么用的是谁的?)

图片7运行结果:图片8

总结:
a)Trait会覆盖当前类继承的方法
b)当前类的方法会覆盖Trait的方法
即:调用类>Trait>父类

4.多个Trait冲突问题(ps:当多个Trait中方法名相同时怎么处理?)

如果直接使用,会产生致命错误
因此需要这样解决
图片9运行结果:图片10

由此看出
a)这里声明Test中的testA方法替换Test1中的TestA方法
b)Test1中的testA方法起别名为bb
总结:
当出现冲突时可以使用insteadof来确定使用哪一个方法
使用as就可以使用另一个方法

相关测试

a)直接其别名是别名不使用insteadof声明是否可以? (否,会报错)
b)别名可以单独使用? (可以)
c)当出现三个或三个以上时怎样处理?
图片11运行结果:图片12
ps:少一个依旧会报错

5.Trait嵌套(Trait中包含Trait)
图片13运行结果:图片14
总结:
依旧使用use可以实现相互嵌套

相关测试
a)如果冲突是否和类中解决一样? 是的,不处理会报错
b)Trait中的方法与嵌套中的方法吗一样,那么是冲突还是按优先级分?
图片15运行结果:图片16
可见是按当前的算。

6.Trait支持抽象方法、支持静态方法,不可以直接定义静态变量,但静态变量可以被Trait方法引用
图片17运行结果:图片18

ps:当定义抽象方法时,必须在使用它的class中实现,否则会产生致命错误.
当然Trait中也可以定义属性。不过如果类中定义了和他不同的属性也会报致命错误。

从上面可以看出,Trait既不是接口也不是类。主要是为了解决单继承语言的限制。是PHP多重继承的一直解决方案。
比如同时要继承两个Abstract Class将会很麻烦,而Trait就是为了解决类似问题。它能被加入到一个或多个已存在的类中。它声明了类能做什么(表面了其接口的特性),同时也包含具体的实现(表面了其类的特性)

总的来说,个人感觉就是Trait = abstract class - interface

HTML5实用标签属性用法

整   理:晋 哲

时   间:2017-02-23

说   明:介绍<picture>和<details>HTML5新标签的实用用法

一、响应式显示图像
在<picture>标签里会先识别<source>里的路径,当满足media条件时显示srcset里的图片,当不满足条件则显示<img>里的图片。

<picture>
    <source srcset="images/pic2_1.png, images/pic2_2.png 2x"
            media="(min-width: 200px) and (max-width:320px)">
    <img src="images/pic1_1.png" srcset="images/pic1_2.png 2x, images/pic1_3.png 3x" />
</picture>

1、<source>元素用于创建可替换的图像文件;

2、media属性:用来包含想要展示的特性,比如视口的当前高度(viewport height),宽度(width),方向(orientation)等;

3、srcset属性:与相应的图像文件名相匹配,进行加载。如果需要提供不同的像素密度,可以添加多个的文件名到srcset属性中。

提示:可以放置多个<source>标签,以指定不同的图像文件名,进而根据不同的条件进行加载。
兼容性:
compatibility1

二、显示或隐藏下拉菜单
在<details>标签里会默认先隐藏<summary>标签以外的html内容,点击<summary>标签内容则显示出来,形成下拉效果。

<details>
    <summary>Click me</summary>
    <dl>
        <dd>list1</dd>
        <dd>list2</dd>
        <dd>list3</dd>
    </dl>
</details>

提示:请与<details>标签一起使用。<summary>标题是可见的,当用户点击标题时会显示出详细信息。
注释:"summary"元素应该是"details"元素的第一个子元素。
兼容性:
compatibility2

分析页面ajax请求时间过长问题

标签(空格分隔): 性能


前提:
商城的首页是采用装修做的,页面会有很多的商品块,采用ajax局部加载的方式来绘制到页面上,但是部分ajax的请求速度非常慢,找出问题原因并解决


此处输入图片的描述
上图可以看出:ajax请求加载页面时,尽管会获取的内容很小(几百B)但是网络相应时间却非常长,主要集中在waiting(TTFB)

waiting(TTFB)解释:
从客户端开始和服务端交互到服务端开始向客户端浏览器传输数据的时间(包括DNS、socket连接和请求响应时间),是能够反映服务端响应速度的重要指标,获取在接收到响应的首字节前花费的毫秒数。


本次主要从程序方面来分析:即服务器接受请求开始到返回数据给客户端为止
1、看页面ajax请求的页面是否本身加载速度很慢
2、在页面请求过程中,除了本身请求的商品数据之外,是否还有其他的内容需要加载
3、解决上述两点问题,观察页面的响应时间


解决办法:
1、通过xhprof来进行性能分析,发现商品信息加载只耗费了43ms左右的时间,钩子中的商城数据消耗约417ms的时间,对钩子数据接口进行缓存,将钩子的请求时间降到45ms左右
重新观察页面,部分ajax请求的时间仍然在1-2s左右。

2、试着减少页面的ajax强求,发现响应时间会有变化,怀疑页面ajax请求过多导致,google:AJAX请求使PHP反应时间过长的问题。

PHP自带Session隐患(session文件独占锁引起阻塞):当服务器发送一个Ajax请求时,PHP脚本开启了session_start(),它的调用会锁定PHP的session文件。PHP默认会把session数据存储在服务器上的文件中。因为仅仅只有一个PHP请求能改变同一个session文件,两个同时的PHP请求可能会造成典型的文件锁条件。

大部分PHP框架会首先在主文件中使用session_start()。因此,如果使用会调用session_start()的框架或者函数库,将会造成session文件锁,对于使用同一个浏览器的相同用户,这将延迟同时发送的Ajax请求。

结合了PHP的Session机制,找到了阻塞的原因。由于PHP的Session信息是写入文件的,1个客户端占有1个session文件。因此,当session_start被调用的时候,该文件是被锁住的,而且是以读写模式锁住的(因为程序中可能要修改session的值),这样,第2次调用 session_start的时候就被阻塞了。

    最简解决方法:
PHP的手册session_write_close函数,作用是Write session data and end session。因此,我们可以在用完session之后,调用这个函数关闭session 文件即可解除锁定。

调用session_write_close()之后,当前脚本会继续正常运行,但在调用session_write_close()之后不允许改变任何session变量;在同一个脚本中,其它同时发送给PHP的请求可以锁定session文件并改变session变量。


备注:
session数据通常会在脚本执行结束后被保存,而并不需要调用session_write_close(),但是为保护session在任何时候都只能被一个脚本执行写操作,session的数据会被锁住。当同时使用框架网页和session时你会发现,框架里的网页会因为这个个锁定而逐个载入。可以通过在所有的session数据修改保存结束后马上结束session来加快载入时间。