lnmp团队开发环境docker环境搭建 - 风.foxwho(神秘狐) - 博客频道 - CSDN.NET

mikel阅读(837)

docker-compose-nginx-php-mySQLdocker-compose+nginx+php+mySQL+redis+elasticsearch fox.风github库https://github.com/foxiswho/docker-compose-nginx-php-mySQL 喜欢的同学可自行下载版本php:5.6.xmysql:5.6.xnginx:latestela

来源: lnmp团队开发环境docker环境搭建 – 风.foxwho(神秘狐) – 博客频道 – CSDN.NET

docker-compose-nginx-php-mysql

Docker-compose+nginx+PHP+MySQL+Redis+elasticsearch
fox.风

github库

https://github.com/foxiswho/docker-compose-nginx-php-mysql
喜欢的同学可自行下载

版本

php:5.6.x

mysql:5.6.x

nginx:latest

elasticsearch:latest

说明

目前只针对MAC系统的安装,其他系统请自定修改 Volumes 和 Users 目录

安装方式

1.选择要安装的系统和版本

例如:mac系统 php5.6.x版本,进入mac-php56目录

cd mac-php56
  • 1
  • 1

2.docker-compose.yml配置更改

选择你需要的功能,不需要的用#号注释掉

3.生成镜像

这个时候 一定不能更换目录。

注意 如果你以前已经配置过一样的容器那么会自动覆盖容器

docker-compose up
  • 1
  • 1

时间比较长, 更新源都在国外,最好早晨拉取

nginx

配置文件位置:/etc/nginx/nginx.conf

来源:官方源

php

来源:官方源

php-fpm

配置文件及目录位置

/usr/local/etc/php-fpm.conf

/usr/local/etc/php-fpm.d/www.conf

/usr/local/etc/php-fpm.d 目录

php.ini 默认加载目录位置

/usr/local/etc/php/conf.d/ 目录

redis

来源:官方源

elasticsearch

来源:官方源

使用教程-MAC系统

新建目录

/Volumes/work/lanmps/vhost/
  • 1
  • 1

work:分区名称

lanmps:自定义目录

vhost:nginx 站点配置文件

复制 nginx/default.conf/Volumes/work/lanmps/vhost/ 目录下

1.mac硬盘设置

mac 系统默认硬盘 不区分大小写,

如果需要区分请 按 http://blog.csdn.net/fenglailea/article/details/53083785 此链接中教程 重新分区。

建议分区,我这里是分区的

2.nginx 配置

进入nginx容器

docker exec -it compose_nginx_1 bash
  • 1
  • 1

compose_nginx_1 容器名字可能不一样
注意 自动生成的名字是根据当前项目目录名字来的。
例如项目目录为mac-php56目录,那么生成容器前缀名字是macphp56_xxxx
配置nginx.conf

vim /etc/nginx/nginx.conf
  • 1
  • 1

找到如下配置

include /etc/nginx/conf.d/*.conf;
  • 1
  • 1

修改为:(根据你自己分区名称置修改)

include /Volumes/work/lanmps/vhost/*.conf; 
  • 1
  • 1

保存和退出容器

3.php 配置

跟nginx类似

5.测试

/Volumes/work/lanmps/ 目录下
新建 index.php

<?php
phpinfo();
  • 1
  • 2
  • 1
  • 2

X.问题

X.1 权限问题
设置 可读写执行权限

chmod -R 777 /Volumes/work/lanmps

使用 docker 部署你的 Laravel 项目 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 - Powered by PHPHub

mikel阅读(1516)

来源: 使用 docker 部署你的 Laravel 项目 | Laravel China 社区 – 高品质的 Laravel 和 PHP 开发者社区 – Powered by PHPHub

使 docker部署laravel项目

一.安装docker#

  • 一.安装docker
  • 二.下载镜像
  • 三. 自定义php镜像
  • 四. 运行自己的应用环境

    我使用的是centos 7.2,在centos下安装docker是件非常容易的事情,在命令行执行:

yum install docker

完成安装之后开启docker和加人开机自启动,可以使用 命令:

service docker start
chkconfig docker on

此处采用了旧式的 sysv 语法,如采用CentOS 7中支持的新式 systemd 语法,如下:

systemctl  start docker.service
systemctl  enable docker.service

看看你的docker安装结果,运行命令:

docker —version

显示以下提示,那么就恭喜你安装成功

Docker version 1.10.3, build cb079f6-unsupported

二. 下载镜像#

docker官方有dockerhub 提供安全切完善的镜像下载,而且docker提供了很方便的方式可以在docker客户端执命令即可。首先分析我们需要哪些docker镜像才能完成项目的部署。我们需要一个提供http服务的container,一个mySQLcontainer,一个rediscontainer,一个php-fpmcontainer,最后还有一个数据卷container,那么我们就需要去下载这些镜像,在命令行下可以使用docker search Images_name 去搜索你想要的镜像

首先pull 基础镜像Ubuntu的

docker pull docker.io/ubuntu

由于laravel框架使用php版本比较高,所以我建议大家都使用php7以上的版本,而且我这里使用了redis,需要安装redis扩展,PDO扩展,XML扩展等等,所以我决定自己build一个image。那么就开始吧

首先创建一个Dockerfile文件,输入以下内容

FROM ubuntu:latest

RUN apt-get update && apt-get install -y software-properties-common language-pack-en-base \
    && LC_ALL=en_US.UTF-8 add-apt-repository -y ppa:ondrej/php \
    && apt-get update \
    && apt-get install -y php-xml php7.0 php7.0-fpm php7.0-mysql mcrypt php7.0-gd curl \
       php7.0-curl php-redis php7.0-mbstring sendmail supervisor \
    && mkdir /run/php \
    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

RUN sed -i -e 's/listen = \/run\/php\/php7.0-fpm.sock/listen = 0.0.0.0:9000/g' /etc/php/7.0/fpm/pool.d/www.conf \
    && sed -i -e 's/;daemonize = yes/daemonize = no/g' /etc/php/7.0/fpm/php-fpm.conf

WORKDIR /var/app

EXPOSE 9000

CMD [“/usr/bin/supervisord"]

除了Dockerfile以外,我们还需要一个supervisor 文件,放在Dockerfile同级目录。

那么就创建一个supervisor文件吧。内容如下:

[supervisord]
nodaemon = true
logfile = /var/log/supervisord.log
logfile_maxbytes = 10MB
pidfile = /var/run/supervisord.pid

[program:php-fpm]
command = php-fpm7.0
user = root
autostart = true
autorestart = true

这里我们的php-fpm镜像还没有完成,还需要build,输入命令:

docker build -t php7-fpm -f Dockerfile

另外一个需要自定义的镜像就是nginx,我就不废话那么多了,直接贴Dockerfile,另外我们需要加载一个nginx的配置和一个nginx网站的配置,玩过nginx的同学都懂啦。

FROM docker.io/nginx
RUN mkdir -p /var/app \
    && chown -R nginx:nginx /var/app
WORKDIR /var/app
COPY nginx.conf /etc/nginx/nginx.conf

接下来给你们分享一下我的nginx的配置和网站配置,nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    gzip  on;
    gzip_disable "MSIE [1-6].(?!.*SV1)";

    server_tokens off;
    include /etc/nginx/conf.d/*.conf;
}

请记住你上面设置的 user 因为dockerfile里面设置工作目录要讲nginx用户权限给它

继续给网站配置吧default.conf

server {
    listen 80;

    # 用真实的域名替换 “_”
    server_name your_domain_name;

    charset utf-8;

    root   /var/app/public;
    index  index.php index.html index.htm;

    gzip on;
    gzip_disable "msie6";
    gzip_types text/plain text/css text/xml text/javascript application/json
        application/x-javascript application/xml application/xml+rss application/javascript;

    error_page 404 = /index.php;

    access_log off;
    error_log /var/log/nginx/error.log crit;

    client_max_body_size 64m;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location /. {
        return 404;
    }

    location ~ \.php$ {
         include fastcgi_params;
         try_files $uri =404;

         fastcgi_pass php:9000;
         fastcgi_split_path_info ^(.+\.php)(/.+)$;
         fastcgi_read_timeout 300;
         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
         fastcgi_index index.php;
    }

    location ~ /\.ht {
        deny  all;
    }
}

请注意,fastcgi_pass php:9000 ,这里不再是127.0.0.1:9000,因为你的php解析交给php-fpmcontainer处理,所以这里的php就是php-fpm的别名

OK,需要自定义的两个container已经完成,下面另外两个,redismySQL 就直接使用docker 官方提供的镜像。

直接使用命令拉下来吧:

docker pull docker.io/redis
docker pull docker.io/mysql

接下来比较重要,因为我们的container运行有依赖关系的,首先,php运行需要linkmySQLredis的,nginx启动是要linkphp上的,请允许我用依赖这个词来简单的说吧。所以首先要启动redismysql两个container,再启动php,等php启动之后最后才能启动nginx

接下来我就用我学到的基础知识,来让你们把laravel运行到docker上吧

首先我们启动redismysql,这里我必须说一下的是,这就是使用docker的好处,如果你的mysqlredis突然崩溃,你可以不用担心数据有任何闪失,因为我们再container外挂在了一个数据卷,什么事数据卷可以自己度娘,不多废话,直接上运行命令吧。

那就先启动mysql,我将mysql的data文件挂载到了宿主机上,这样就实时同步备份了container的数据

docker run -d —name=testmysql  -v /var/data:/var/lib/mysql MYSQL_ROOT_PASSWORD=testpassword  -e MYSQL_DATABASE=test_database docker.io/mysql

这里说明一下命令和参数是干嘛的,-d 是后台运行 --name=testmyql 是设置container名称,-v 是挂载宿主机到container -e 设置环境变量传递mysql密码 -e参数是可以多次使用
,例如:-v 前面是你宿主机的路径,冒号是你容器里的路径。

再启动redis,同样也挂载了redis的备份文件

docker run -d --name redis -v /var/www/mydoc/shares/dump.rdb:/data/dump.rdb docker.io/redis

有了redismysql,我们就可以启动php-fpm了

docker run -d --name php -v /var/www/mydoc/shares/:/var/app --link mysql:mysql --link redis:redis php-fpm

这里除了上面加的参数以外,我们使用了—link 这个是链接两个容器,使两个容器能够通信

最后我们运行nginx来提供http服务

docker run -d -p 80:80 -v /var/data/conf/:/etc/nginx/conf.d/ --volumes-from php —link php:php nginx

这里解释一下 --volume-from ,它的意思是数据来自php这个container

OK ,至此,容器基本上都启动完毕。当然,如何将网站跑起来,需要你自己思考,学习嘛。必须自己动动脑子,当然我这个也是比较基础的。希望大神看到勿喷。

欢迎加QQ:343125118 交流.

最后我也提供一下我已经构建好的镜像给大家使用,我已经放在dockerhub上了,不需要上面的dockerfile去构建,直接使用就可以:

docker pull docker.io/beandu/php7-fpm
docker pull docker.io/beandu/nginx

git 的使用(4)-git暂缓区工作区原理和修改删除等命令 - 魂灵舞 - 博客园

mikel阅读(953)

来源: git 的使用(4)-git暂缓区工作区原理和修改删除等命令 – 魂灵舞 – 博客园

文章转载自:http://blog.csdn.net/think2me/article/details/39056379

博主说未经本人允许,不得转载,那就不贴了,拷贝关键部分作备忘

 

1. 暂存区是Git非常重要的概念,弄明白了暂存区,就弄明白了Git的很多操作到底干了什么。
2. git 管理的是修改,不是文件,所以commit只会提交已经add的暂缓区的修改
3. 修改:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout — file

4 .修改:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,在用git checkout — file

5. 修改:已经提交了不合适的修改到版本库时,想要撤销本次提交,要用 git reset — hard commit_id 来版本回退

6. git rm用于你确认删除一个文件

 

博主贴了个全能git教程 http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000

angularjs select ng-options延迟更新

mikel阅读(1228)

Angular是Google开发的前端技术框架。

来源: Angular中文社区 | Angular中文社区

在使用angularjs的ng-options时,在后台更改了scope里对应的数组后,前段的select里的option不会马上更新,而是选择了某项后才会更新

前端代码如下

<select ng-options="option.id as option.name for option in options"
    ng-model="item" ng-change="update()">

Controller里的代码

$scope.options = [{name:'aa',id:1},{name:'bb',id:2}];//初始化时候的值
socket.on('sites',function(data){
        $scope.options = data;//socket.io更新该值
    });

现在的问题就是socket.io更新完$scope.options前端select里的值不会马上更新,要点下更改select里的选项后才会更新

其实并不是“在更改了select里的选项后才更新”,而是因为响应了sites事件后的数据更新操作被没有被视图得知。因为你的socket实际是个游离在$scope之外的操作,所以双向绑定在这里没有生效。

那为什么”在更改select之后”它却刷新了呢?原因也简单,因为你这次的操作又重新回到了$scope的生命周期之中,所以双向绑定监测到了$scope.options的变化。

解决方法超简单,就是强制通知angular你的数据更新了:

$scope.options = [{name:'aa',id:1},{name:'bb',id:2}];//初始化时候的值
socket.on('sites',function(data){
        $scope.options = data;//socket.io更新该值
        $scope.$apply(); //强制通知
    });

实时开发框架Meteor API解读系列 Meteor.methods - 博客频道 - CSDN.NET

mikel阅读(920)

来源: 实时开发框架Meteor API解读系列 Meteor.methods – 博客频道 – CSDN.NET

写在前面的话

  1. 该系列是零散的。 写到哪个是哪个 不会有个顺序。
  2. 尽量一篇博客一个API
  3. 尽量搭配一个代码实例 (因为时间比较零碎不一定能够保证)
  4. 每篇博文我会标记所使用的Meteor版本,防止因为API变化,导致博客的内容的正确性问题
  5. 能够看懂官方API就尽量看官方的,官方的才是最准确的。我这里只是提供一个参考

V 0.7.01

本篇文章涉及到的API:Meteor.methods,Meteor.call,Meteor.apply,Meteor.starup


Meteor.methods — Anywhere

关于 Meteor.methods 先看一下官方的简单描述

Defines functions that can be invoked over the network by clients.

大概意思就是 定义一些方法可以被客户端通过网络进行调用。注意下面的解释都是以这一条为基础来解释的。

ps:因为我初次看官方文档就糊涂了。不知道大家注意了没有,官方文档在每个API 后面有个标记,标记api的使用范围是在客户端(Client)使用还是服务端(Server)使用,亦或是同时都可以使用(Anywhere)。

Meteor.methods后面的标识是Anywhere我的第一反应是服务端通过Meteor.methods定义的方法客户端可以调用,客户端通过Meteor.methods定义的方法服务端可以调用。其实如果稍微思考一下就会知道这样想是错误的。因为客户端有那么多,Meteor怎么知道到底使哪个客户端来执行这个方法呢? 但是这里的Anywhere是什么意思呢?稍后解释。

    *强调 API的作用也可以说它的主要目的是实现: 在服务端定义一组方法,使客户端能够调用。换句话说,通过这个API,客户端可以命令服务端做一些我们允许做的事情。
client 端定义的Meteor.methods叫做 'stub'(官方用的是这个单词) ,至于怎么翻译,看个人理解吧。这是下文需要用的一个描述。[我翻译成'存根',理解为 对服务端方法调用的客户端补充动作。]

参数:Object
函数Meteor.methods的参数是一个object对象,这个对象里面的属性名就是供客户端调用的函数名,该属性对应的值就是共调用的函数。
例如:

{
    "hello":function(){..},
    "welcome":function(){..},
    "bey":function(){..}
}

如下使用:(服务端)

 Meteor.methods({
        "say":function(name){
            //do somthing
            console.log("Hello " + name);
            return "Hello boy";
        } ,
        "draw":function(...){
            .....
        },
        .....
 });

返回值:JSON对象(官方原文是: EJSON-able value)或者一个Meteor.Error对象

Meteor.Error 当后台处理到异常时,应该主动抛出的出错信息。这是一个Meteor自定义的一个异常对象。这个对象与本篇主题不相关,暂不做解释。可能一篇博客介绍它。

在被调用函数的内部 (如say对应函数的内部)存在一个上下对象 this。这个对象组成如下:

{
    isSimulation: Boolean// a boolean value, true if this invocation is a stub.
    unblock: Function,//when called, allows the next method from this client to begin running.
    userId: String,//the id of the current user.
    setUserId:  Function//a function that associates the current client with a user.
    connection: Object//on the server, the connection this method call was received on.
}

根据官方的解释可能不是很i清楚,后面我们通过代码进行测试。

现在Meteor.methods的参数类型,和其中的函数应用已经讲,那么接着讲 如何来调用这些在Server端定义的内容。

有两个方法可以使用

1. Meteor.call
2. Meteor.apply

(个人认为:这两个方法的区别有点类似js的call与apply的区别)

Meteor.call — Anywhere

具体形式如下:

Meteor.call("function name",arg1,arg2,..,function(error,result){
    //do something
});

function nanme 表示 在Meteor.methods中定义的对象的属性名,通过这个属性名来表示调用服务端对应的这个方法。

arg1,arg2,.. 是任意个参数,这个参数将作为服务端函数的参数。

注意这些参数仅为json或者一般数据类型,不能传递包含function对象

最后一个是回调函数。如果服务端函数抛出异常则 将赋值给error这个参数。
如果没有异常抛出,则服务端函数的返回值将赋值给result这个参数。

接下来看具体实现和代码。

meteor create api-001 #创建meteor应用
cd api-001 #进入应用目录
rm *.html *.css *.js #删除自动生成的.html,css,js文件
mkdir server #创建server文件夹
mkdir client #创建client文件夹

(关于文件夹的作用请参考其他人博客,或官方文档,我暂时还没有写。)
在server文件夹中新建一个js文件,如 main.js 内容如下:

Meteor.methods({
    "test":function(){
        console.log("Hello  I'm Server");
    }
});

现在代码已经完成了,运行这个工程。
用浏览器(建议使用Chrome,该系列博客都将以此浏览器进行)打开http://localhost:3000 按F12 进入开发者模式 找到console控制台,输入:

Meteor.call("test")

这时在meteor启动的命令窗口将看到字符串输出。这就是Meteor.call的使用了。客户端调用服务端的方法。

(这里使用chrome控制台来完成相关测试是为了尽量保持博客的简洁性。同样的语句当然也可以写到客户端的js文件中,然后在html中引用,调用。效果是一样的,但是对于这篇博客来说 代码量就显的繁琐了。关于chrome的开发者模式的使用,请查找资料,或者查看我的博客如果你在看这篇博客时我已经写了的话)

最简单的demo已经了解过了,接下来看下它的返回值和参数的传递。
改动一下 main.js:

Meteor.methods({
    //这里的参数可以是N个
    "test":function(a,b){
        console.log(a + " " + b);
        //这里使用的返回类型是json对象,当然可以使用基本数据类型,如string,number,bool等
        return {
          a:a,
          b:b,
          c:a+b
        }
    }
});

meteor自动重启。同样在chrome控制台输入:

Meteor.call("test",1,2,function(error,result){
    //服务端抛出的异常将被这个回调函数的地一个参数接收,也就是error
    //当服务端无异常抛出时,error为undefined
    if(error){ 
        console.log("服务端异常");
        console.log(error);
        return;
    }
    //服务端函数的返回值将被第二个参数接受,也就是result
    console.log(result);
});

打印结果很明确就是服务端的返回值。

注意,因为Meteor.call可以在ServerClient同时使用,
    Server端运行Meteor.call时,最后一个参数不是回调函数的话,在可能的情况下服务端将同步执行这个方法体)验证代码如下  main.js

var fs = Npm.require('fs');
Meteor.methods({
    "test":function(a){
        return a;
    }
});

Meteor.startup(function(){
  var s = Meteor.call("test",5);
  console.log(s);
});

    强调!这仅是在Server可以这样用,在Client如果要取到Server的返回值,必须使用回调函数才能获取到。
------------------------------------
    现在,下面的这个结论如果不能理解或者对你造成了迷惑请忽略它,因为它的实际作用不大。只是因为API中对它有个简单的提及,那么就用代码代码验证一下。
    文档原文:
    "On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method." 
    原文就是上面这样了。不知道你思考了没有我上面提及到的这句话伸展:"在Client如果要取到Server的返回值" 什么伸展呢?因为Meteor.methods在客户端也可以使用,那么如果Client Meteor.call调用ClientMeteor.methods中的函数 是不是也需要用回调函数呢? 结论是:不一定需要。
    但是根据原文文档(原文没有直接提及,是根据"you do not pass a callback and you are not inside a stub"推断出来的,所以说这个结论实际作用不大),你得满足一个条件才行。条件是:你的Meteor.call必须在stub[前文已经解释过]中。(当然Client Metoer.methods本身不能涉及到异步调用。这个和Server端的同步是同样的样要求),而且在sub中使用Method.call 只会调用Client Meteor.methods中定义的方法,而ServerMeteor.methods不会执行。
官方文档提到了这个上面的这个情况。原文如下:
 "Finally, if you are inside a stub on the client and call another method, the other method is not executed (no RPC is generated, nothing 'real' happens). If that other method has a stub, that stub stands in for the method and is executed."

通过以下代码的运行就能看到:
Server/main.js

Meteor.methods({
  hello:function(){
    console.log("server");
    return 10;
  }
});

Client/client.js

Meteor.methods({
  hello:function(){
    console.log("clent");
    return 12;
  },
  say:function(){
    console.log('say');
    var hello = Meteor.call("hello");//Server下的hello 不会执行。
    console.log(hello);//打印值是12
  }
});


Meteor.apply — Anywhere

服务端代码不需要改动,在浏览器控制台输入:

Meteor.apply("test",[1,2],function(errror,result){
    if(error){
        //do something;
        return;
    }
    // do somthing 
    console.log(result);
});

可以看到 Meteor.apply和Meteor.call 在一般用法 上基本没区别,仅是传递参数的方式不同而已,一个使用参数列表,一个使用参数数组。其实对于大多数 开发过程而言 掌握上述两种的使用已经能够满足开发需要了。因此对无更多要求的人而言,本篇博客可以到此为止。前文提到的 服务端函数 内部的上下文对象的使用 和 关于Meteor.methods的Anywhere 这两个还没解释的地方 可以忽略了。

Meteor.methods的Anywhere

大家细心一点可能发现Meteor.call,Meteor.apply同样是”Anywhere”.

先解释一下Server的call,apply和Client的call,apply的区别。

区别很简单:

    Server
        仅能调用服务端的Meteor.methods中定义的函数
    Client
        同时调用服务端和客户端的Meteor.methods中定义的函数。这个涉及到了Meteor.methodsAnywhere了。具体请往下看。

因为前面已经介绍了methods,call,apply的简单用法了。那么下面的代码就尽量简化不再描述这些了。

首先修改server的main.js。内容如下:

Meteor.methods({
    "test":function(a){
      console.log("server");
      return  a+1;
    }
});
//Meteor加载完所有文件后将执行这个方法。具体使用请参考官方文档,比较简单。
Meteor.startup(function(){
    //然meteor启动后执行test方法
    Meteor.call("test",1,function(err,result){
        console.log(result);
    });
});

另外,最开始的时候就新建了一个client文件,在该文件夹下 新建一个js文件,如client.js
内容如下:

Meteor.methods({
    "test":function(a){
        console.log("Client");
        return a+"aaa";
    }
});

代码基本就这样 然后重新运行meteor.

可以看到在Meteor.startup调用的Meteor.call只执行了Server下定义的test函数。

接着打开Chrome的控制台,输入:

 Meteor.call("test",1,function(err,result){
        console.log(result);
 });

可以发现,客户端Meteor.methods和服务端Meteor.methods定义的test方法都执行了。而且在这个call函数的里回调函数的返回值 是服务端test的返回值,客户端test函数的返回值自动被舍弃了。

如果ClientServer同时定义了Meteor.methods 函数,在进行调用时,server 执行Meteor.callMeteor.apply 只会使Server端的Meteor.methods中定义的函数执行
,而Client端执行的callapply 会同调用客户端和服务端中Meteor.methods定义的函数。
且返回值都是以Server端的函数返回值,客户端的返回值将自动舍弃。其实这个结论自己多写几个代码就能够了解。

到这里 Meteor.methods的Anywhere不用再解释,应该也明白了。

Meteor.methods中函数的上下文对象 this

this中包含五个属性如下 (这些当需要用到时,能够想起有怎么个属性存在就可以了。到时候会自动知道该怎么使用)

{
    isSimulation: Boolean// a boolean value, true if this invocation is a stub.
    unblock: Function,//when called, allows the next method from this client to begin running.
    userId: String,//the id of the current user.
    setUserId:  Function//a function that associates the current client with a user.
    connection: Object//on the server, the connection this method call was received on.
}
  1. this.userId
    调用这个函数的用户的id ,类型:string。如果用户没有登录,值为null。 该属性在 Server的Meteor.methods和Client的Meteor.methods都存在。用官方的描述就是Anywhere。 这个属性依赖Meteor的accounts 模块。这里不赘述。
  2. this.setUseId 类型:Function,该setUerId函数的接收一个字符串或者null。使用范围:Server 设置当前链接的 用户。也就是说可以改变登录用户,或者使用户登录[如果this.userId为null],注销登录用户[this.setUserId(null)] .同样需要 Meteor的accounts模块的支持。
  3. this.isSimulation 类型:Boolean 。使用范围:Anywhere
    这个属性 我解释不好(如有好的理解请在博客下方留言),官方的解释是这样的:
Access inside a method invocation. Boolean value, true if this invocation is a stub.

[我理解意思是在Client的Meteor.methods中该值为true,Server中为false]。而且经过代码如下代码测试貌似和我理解的意思差不多 :

1. Servercall apply调用时,在ServerMeteor.methods```isSimulation```false

2. Clientcallapply调用时,
    ServerMeteor.methods```isSimulation```false
    ClientMeteor.methods```isSimulation```true

测试代码:
client.js:

Meteor.methods({
    "test":function(a){
        console.log("Client");
        console.log(this.isSimulation); //false
        return a+"aaa";
    }
});

main.js:

Meteor.methods({
    "test":function(a){
      console.log("server");
      console.log(this.isSimulation);//true
      return  a+1;
    }
});

Meteor.startup(function(){
    Meteor.call("test",1,function(err,result){
        console.log(result);
    });
});

this.unblock()
解释这个之前,我刚才得到了一个结论,一直没有注意到(没有使用过this.unblick()也没注意过它是否是阻塞的,写博客确实也是一种自我学习过程,当然这是题外话了。)的结论。默认情况下在Client调用Server端的函数这种过程是阻塞式的。意识就是说,如果有多个客户端同时调用test这个函数,而test这个函数的执行需要耗费一段,那么这个调用会进行排队依次执行。而就是只要当前面N次调用都完成以后,后面的调用才会进行。可以经过一下代码验证:
清空client.js的内容,不需要。 然后main.js的内容是:

var fs = Npm.require('fs');
Meteor.methods({
    "test":function(a){
      if(a==1){
        //这里我通过多次读写文件的时间耗费进行阻塞
        //,注意文件大小最好是在10M-40M之间,太小的文件,或者一般的计算可能执行速度太快看不到效果
          for(var i=0;i<20;i++){
            var data = fs.readFileSync("/home/ec/download/NVIDIA");
            fs.writeFileSync("/home/ec/download/test/NVIDIA"+i, data);
          }
      }
      console.log(a);
    }
});

在单个浏览器端,或者同时多个浏览器运行:(把命令写在一行)

 Meteor.call("test",1); Meteor.call("test",2);

这样就可以看到阻塞的效果。[我猜大概这样是为了实现类似资源锁之类操作]

那么现在,我们不想它阻塞在这里也是很容易做的。没错!就是在函数的第一行执行:
(这里貌似存在一个bug 这个this.unblock()没有起作用 ,具体原因可参考 详情 和 解决方法

this.unblock();

这是main.js内容如下:(参考解决过后的代码)

var fs = Npm.require('fs');
var readFile = Meteor._wrapAsync(fs.readFile.bind(fs));
var writeFile = Meteor._wrapAsync(fs.writeFile.bind(fs));
Meteor.methods({
    "test":function(a){
      this.unblock();
      if(a==1){
          //这里我通过多次读写文件的时间耗费进行阻塞
          //,注意文件大小最好是在10M-40M之间,太小的文件,或者一般的计算可能执行速度太快看不到效果
          var data = readFile("/home/ec/download/NVIDIA");
         for(var i=0;i<5;i++){
            writeFile("/home/ec/download/test/NVIDIA"+i, data);
            console.log(a+"-"+i);
        }
      }
      console.log(a);   
      return a;
    }
});

好了,this.unblock()的使用是这篇博客多耗费了2天时间。总算跳过去了。现在就剩最后一个点了。

this.connection Server

这是一个Connection对象,具体可以参考Server connections和这个部分的Meteor.onConnection(callback) 此属性不再本篇博客的讨论范围内。既然不讨论,那么就抄个官方文档的解释放在这里吧。

Access inside a method invocation. The connection this method was received on. null if the method is not associated with a connection, eg. a server initiated method call.

其中一个表达的大概意思是 如果没有通过http(非Client-Server或DDP)而是直接在Server中来调用这个methods里面的方法,那么connection是一个null。

本篇博客结束了。

对了最后其实还落下了一点点。那就是关于Meteor.apply(name, params [, options] [, asyncCallback])第三个可选参数options的解释了。
应该比较好理解 。同样贴上一个官方文档。

Options #仅在client端才有此参数
wait Boolean
(Client only) If true, don’t send this method until all previous method calls have completed, and don’t send any subsequent method calls until this one is completed.
onResultReceived Function
解释:如果为true,除非前面的方法调用都已经完成回调了,才会开始执行本次的函数调用。并且在本次函数调用完成之前 不会在执行其他任何的函数调用了。
(Client only) This callback is invoked with the error or result of the method (just like asyncCallback) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method.

这个的验证代码可以自己尝试写一下。就当是思考题吧。

转载请注明出处。谢谢!

使用Docker搭建Laravel环境 - 简书

mikel阅读(1278)

来源: 使用Docker搭建Laravel环境 – 简书

现在有很多关于Docker的争吵,这是正常的。因为Docker带来了一个重大飞跃,app的集装箱化活用,将搭建例如google和facebook那样强大的数据中心的能力带到了普通的开发者或系统管理员的手中。

Docker使用源生Linux内核集装成一个进程,类似于将它们放入一个独立的小仓库里面,使得它们不会影响到系统的其他进程。Docker常被用于部署,管理,和自动化构建容器。集装箱化,带来和虚拟机相似等级的隔离,却只耗费运行虚拟机管理程序的10%-15%的性能。看下面这部Docker创始人兼Docker的CTO的访谈,可以帮助你快速了解Docker。(油管链接,我就不贴了)。如果你想要了解更多背景,你可以访问Dokcer的官网。

我开发的大多数应用程序都是基于Laravel,所以我想看看使用Docker搭建一个本地开发环境,作为我的生产环境的一个镜像。当你仅仅部署应用程序本身和它的依赖(像Nginx),与部署在传统的虚拟机上相比,安全和稳定的风险将大大降低。而你仍然可以节省从开发环境到生产环境的时间.

网上很少文章是结合Larvel和Docker来讲的.所有我们阅读到的要使用Vagrant的概念,都可以直接使用Dokcer,本文中所有的进程将运行在一个容器中.因为一些原因,这意味着你将错过了一些Dokcer的真正优点.我们想要一个进程运行在一个容器中,我们将每个容器(即进程)链接一个数据容器上,所有的应用程序文件都保存在这个数据容器上.让我们开始吧.

准备你的开发环境

Docker使用集装箱化技术专用于Linux, 所以如果你使用OS X或者Windows,则需要虚拟机.非linux的Docker包被称之为Boot2Docker.
(注:Docker已经宣布支持OS X和Windows了)

关于不同系统版本的安装方式,参见Dokcer的官方文档.Docker documentation

概述

获取和启动运行Lavavel应用程序,我们不仅仅需要一个可以运行PHP的web服务器,我们也需要能够运行PHP命令行应用程序的composerartisan.也许还有更好运行环境的方式(例如浏览器).但是这是一个好的基础整合Dokcer和Laravel.每个进程都有一个自己的容器.

下面列出了我们要使用Docker镜像:

有分离的composerartisan容器对我们来说真是一大优势,我们可以只选择推送
docker-laravel-data,docker-laravel-nginxdocker-laravel-phpfpm容器
到生产环境.

我画了一副流程图可视化帮助理解,沿着获取数据方向理解如何整合这些容器.

流程图

你可以看到这些容器都将他们的/data定位到了docker-laravel-data容器.又可以
看到是从主目录的~/myapp目录定向到了/data.在~/myapp中我们有两个目录
wwwlogs.

  • www – 包含我们的应用程序文件(例如:public/index.php)
  • logs – 存放Nginx的错误日志文件.

从Docker Hub上拉取这些镜像非常容易,只需要在命令行(或boot2docker虚拟机)中运行
下面的命令:

$ docker pull dylanlindgren/docker-laravel-data && \
  docker pull dylanlindgren/docker-laravel-composer && \
  docker pull dylanlindgren/docker-laravel-artisan && \
  docker pull dylanlindgren/docker-laravel-phpfpm && \
  docker pull dylanlindgren/docker-laravel-nginx && \
  docker pull dylanlindgren/docker-laravel-bower

这些镜像也可以通过获取上面GitHub上的Dockerfile,使用docker build命令来构建.
不过那已经是本教程之外的内容了.

Docker&Laravel实践

我使用2013年的MackBook Pro开发,因此下面说明都是针对OS X环境的.应该很容易改
变一些路径,就可以用于了LinuxWindows了.

创建数据容器

创建两个目录在你的系统中~/myapp/www~/myapp/logs.这个~/myapp/目录映射
到数据容器中,提供给其他容器访问应用程序文件.

如果你已经有了一Laravel app,将它的所有文件放入~/myapp/www目录中.否则我们将
创建一个.

让我们来创建我们的数据容器,并且将~/myapp/映射到这个容器的/data目录上.

$ docker run --name myapp-data -v /Users/dylan/myapp:/data:rw dylanlindgren/docker-laravel-data

运行composer命令行

通过如下的命令执行composer命令行:

$ docker run --privileged=true --volumes-from myapp-data --rm dylanlindgren/docker-laravel-composer
*your composer commands here*

哇!这真是一个好长的命令.谁想输入这么长的命令只是为了简单的运行composer dump-autoload?可以灵活的使用Bash的aliases使命令简短.在.bashrc文件中添加下面的代码.

alias myapp-composer="docker run --privileged=true --volumes-from myapp-data --rm dylanlindgren/docker-laravel-composer"

重新启动终端的会话.你就可以通过myapp-composer命令行来运行composer了.

如果你想要创建一个新的Laravel app,执行下面的命令,使用composer下载Laravel和它的依赖:

myapp-composer create-project laravel/laravel /data/www --prefer-dist

不要忘记适当的给app/storage目录权限,不然你可能会看到运行错误.

运行artisan命令行

artisan命令行的运行方式和运行composer命令行的方法相似:

$ docker run --privileged=true --volumes-from myapp-data --rm dylanlindgren/docker-laravel-artisan
*your artisan commands here*

同样的,让我们增加另一条配置到.bashrc中:

alias myapp-artisan="docker run --privileged=true --volumes-from myapp-data --rm dylanlindgren/docker-laravel-artisan"

重启终端,你就可以通过myapp-artisan来运行artisan了.

Laravel服务

NginxPHP-FPM是分开的两个进程,因此,我们将分开它们不同容器的原因在这之前解释清楚.

首先,我们先创建PHP-FPM的容器,注意这里我们使用-d选项,说明进程将在后台运行.PHP-FPM不是运行不成功,以为是像composerartisan那样退出了,其实它只是在保持运行.我们要把它当做守护进程那样运行,这样我们就可以做其他的事情,例如加载Nginx的容器.

如下运行PHP-FPM:

$ docker run --privileged=true --name myapp-php --volumes-from myapp-data -d dylanlindgren/docker-laravel-phpfpm

我们使用--link选项,当我们创建Nginx容器的时候,链接到这个PHP-FPM容器,通过它们的ip来沟通数据.(准确来说是9000端口)

让我们运行Nginx:

$ docker run --privileged=true --name myapp-web --volumes-from myapp-data -p 80:80 --link myapp-php:fpm -d dylanlindgren/docker-laravel-nginx

最后通过浏览器访问http://localhost就可以看见Laravel的欢迎界面.

欢迎界面

总结

希望通过运行本教程的案例,不仅仅只看到了Dokcer,更应该明白Laravel开发是一个巨大的游戏规则.

原文地址:http://dylanlindgren.com/docker-for-the-laravel-framework/

javascript - Adding a listener on a model in angularjs - Stack Overflow

mikel阅读(783)

changeHandler()

来源: javascript – Adding a listener on a model in angularjs – Stack Overflow

You’ve got 2 options to cover your use case:

Use the ng-change directive

You can write your input like so:

Search: <input ng-model="search.model" ng-change="changeHandler()">

where the changeHandler is a function defined on a scope.

Use a watch on a scope

by writing in your controller:

$scope.$watch('search.model', function(newVal, oldVal){
    console.log("Search was changed to:"+newVal);
    $scope.search.watch = newVal;
  });

Here is a working plunk illustrating both: http://plnkr.co/edit/Jgb2slcBFzLNKK0JFNyo?p=preview

The difference between the 2 approaches is that ng-change will fire only as a result of user’s iteractions with an input while $watch will fire for any model mutation – triggered from the input control or any other change to the model. So you can preciselly choose on which events you want to react.

Meteor+Angular实现轻论坛(2)—实现用户模块 - 简书

mikel阅读(1042)

来源: Meteor+Angular实现轻论坛(2)—实现用户模块 – 简书

添加路由控制

添加用户模块之前,先添加路由控制。我们使用Angular的ui-router模块,添加路由功能。

首先添加ui-router模块到项目:

$ meteor add angularui:angular-ui-router

client文件夹下新建lib文件夹,然后把app.js文件移动到lib文件夹下面,然后把ui-router添加到Angular模块依赖中,修改app.js代码,如下所示:

angular.module('louForum', ['angular-meteor', 'ui.router']);

在client文件夹下创建routes.js文件,通过angular-ui-router定义注册和登录的路由,代码如下:

angular.module('louForum').config(['$urlRouterProvider', '$stateProvider', '$locationProvider',
    function($urlRouterProvider, $stateProvider, $locationProvider) {
        $locationProvider.html5Mode(true);
        $stateProvider
            // 首页
            .state('home', {
                // 定义URL
                url: '/',
                // 指定模板(文件必须以“.ng.html”后缀结尾)
                templateUrl: 'client/home/views/home.ng.html',
                // 指定控制器
                controller: 'HomeCtrl'
            })
            // 注册页面
            .state('register', {
                url: '/register',
                templateUrl: 'client/user/views/register.ng.html',
                controller: 'RegisterCtrl',
                // 设置当前控制器中 $scope 的一个引用为 rc
                controllerAs: 'rc'
            })
            // 登录页面
            .state('login', {
                url: '/login',
                templateUrl: 'client/user/views/login.ng.html',
                controller: 'LoginCtrl',
                controllerAs: 'lc'
            })
            // 退出登录
            .state('logout', {
                url: '/logout',
                resolve: ['$meteor', '$state', function($meteor, $state) {
                    return $meteor.logout().then(function() {
                        $state.go('home');
                    }, function(err) {
                        console.error('Logout error : ', err);
                    });
                }]
            });

        // 浏览器没有匹配到以上URL时,转到此URL
        $urlRouterProvider.otherwise('/');
    }
]);

ui-router通过嵌套的statesviews来控制路由,在HTML文件中,要引用一个链接,直接添加对应的state即可,比如链接登录页面:

<body>
    <div ui-view></div>
    <a ui-sref="login">login</a>
</body>

ui-sref是ui-router的一个指令,用于给HTML模板渲染ui-router定义的链接。而指令ui-view用于渲染HTML页面模板。

Angular通过config()方法调用ui-router$stateProvider方法根据state来配置URL。

$locationProvider.html5Mode(true);用于启用html5 mode,$location有两个控制URL格式的模式: Hashbang mode 和 html5 mode。默认使用 Hashbang mode,html5 mode 的URL格式对搜索引擎更友好,两种模式的对比如下图所示:

此处输入图片的描述

我们使用bootstrap构建前端页面,添加bootstrap到Meteor:

$ meteor add twbs:bootstrap

删除index.ng.html文件,打开index.html,修改代码如下:

<head>
    <base href="/">
</head>

<body ng-app="louForum">
    <div id="header">
        <div class="container">
            <div class="pull-left" id="main-nav">
                <a ui-sref="home">首页</a>
            </div>
            <div class="pull-right" ng-if="$root.currentUser">
                <span>Hi, {{ currentUser.username }}</span>
                <a ui-sref="logout">退出</a>
            </div>
            <div class="pull-right" ng-if="!$root.currentUser">
                <a ui-sref="login">登录</a>
                <a ui-sref="register">注册</a>
            </div>
        </div>
    </div>
    <div class="container" id="main-content">
        <div ui-view></div>
    </div>
</body>

ui-srefangular-ui-router的指令,用于指定路由控制函数中定义的state,以对应其中的URL。ng-if是一个判断表达式结果为true或者false的Angular指令,为true则渲染此HTML元素,为false则不渲染。在head标签中,我们添加了base标签,这是html5 mode 所必须的,指定了URL的根目录。我们还添加了一个带有ui-view属性的div元素,根据不同的URL,对应的Angular模板的内容会加载到这个div中,所以点击切换URL时,页面其实是没有刷新的,而只是更改了ui-view中的内容。

注册

Meteor默认加载了很多包,其中insecure包使得服务端默认会把所有的数据发送给所有的客户端,而且所有客户端都可以修改数据,比如没有登录也可以发帖。为了安全,添加用户之前,我们需要移除insecure包:

$ meteor remove insecure

然后添加Meteor用户管理包accounts-password

$ meteor add accounts-password

这个包有登录、注册、修改密码、邮箱验证等功能,非常实用。

在client文件夹中新建user文件夹,在user文件夹中新建controllers文件夹和views文件夹。controllers文件夹用于存放控制器,views文件夹用于存放模板。每一个由Angular控制的页面,都需要控制器和模板。

创建注册控制器,在controllers文件夹中新建register.js,输入如下代码(通过IDE写代码可以输入中文的哦~):

// 定义注册控制器 RegisterCtrl
// 并传入 $meteor 和 $state
angular.module('louForum').controller('RegisterCtrl', ['$meteor', '$state',
    function($meteor, $state) {
        var vm = this;

        // 定义 user model
        // 注册成功后数据将自动保存到mongoDB
        vm.credentials = {
            username: '',
            email: '',
            password: ''
        };
        // 保存错误信息
        vm.error = '';

        // 用户注册方法
        vm.register = function() {
            // 通过$meteor.createUser方法创建新用户
            // 用户信息会保存到数据库
            $meteor.createUser(vm.credentials)
            .then(function() {
                // 注册成功后跳转到首页
                $state.go('home');
            }, function(err) {
                vm.error = 'Register error : ' + err;
                console.error(vm.error);
            });
        };
    }
]);

然后再创建注册的模板,在views文件夹中新建register.ng.html,输入如下代码:

<form class="form-horizontal">
    <div class="form-group">
        <label class="col-md-2 control-label">昵称</label>
        <div class="col-md-10">
            <input type="text" class="form-control" ng-model="rc.credentials.username"><br>
        </div>
    </div>
    <div class="form-group">
        <label class="col-md-2 control-label">邮箱</label>
        <div class="col-md-10">
            <input type="email" class="form-control" ng-model="rc.credentials.email"><br>
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-md-2 control-label">密码</label>
        <div class="col-md-10">
            <input type="password" class="form-control" ng-model="rc.credentials.password"><br>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="button" class="btn btn-primary" ng-click="rc.register()">注册</button>
        </div>
    </div>
</form>

关于注册的控制器和模板就完成了。

登录

同样的,创建登录控制器,在controllers文件夹中新建login.js,输入如下代码:

angular.module('louForum').controller('LoginCtrl', ['$meteor', '$state',
    function($meteor, $state) {
        var vm = this;

        vm.credentials = {
            username: '',
            email: '',
            password: ''
        };
        vm.error = '';

        vm.login = function() {
            // 通过验证邮箱和密码是否匹配来判断登录
            $meteor.loginWithPassword(vm.credentials.email, vm.credentials.password)
            .then(function() {
                $state.go('home');
            }, function(err) {
                vm.error = 'Login error : ' + err;
                console.error(vm.error);
            });
        };
    }
]);

然后创建登录的模板,在views文件夹中新建login.ng.html,输入如下代码:

<form class="form-horizontal">
    <div class="form-group">
        <label class="col-md-2 control-label">邮箱</label>
        <div class="col-md-10">
            <input type="email" class="form-control" ng-model="lc.credentials.email"><br>
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-md-2 control-label">密码</label>
        <div class="col-md-10">
            <input type="password" class="form-control" ng-model="lc.credentials.password"><br>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <button type="button" class="btn btn-primary" ng-click="lc.login()">登录</button>
        </div>
    </div>
</form>

由于登录成功后,会自动跳转到首页,这里我们就先添加一个空白主页,在client文件夹下新建home文件夹,在home文件夹下创建controllers文件夹和views文件夹。

在这个controllers文件夹中新建home.js文件,输入如下代码:

angular.module('louForum').controller('HomeCtrl', ['$meteor', '$state', '$scope',
    function($meteor, $state, $scope) {
    }
]);

views文件夹下新建home.ng.html文件夹,输入如下代码:

<p>home page</p>

运行项目:

$ meteor

访问http:localhost:3000/register即可注册新用户,访问http:localhost:3000/login登录。

在虚拟机中新打开一个shell终端,进入louForum目录,输入如下命令,进入Meteor的MongoDB命令行环境:

$ meteor mongo

然后输入如下命令,进入名为meteor(Meteor的默认数据库)的数据库:

> use meteor;

输入如下命令,查看该数据库中的Collection:

> show collections;

可以看到,有一个名为users的collection,输入如下命令,查看users中的数据:

> db.users.find();

可以看到打印出了刚刚注册的用户的数据。到此,注册和登录用户就完成了。

总结

这节实验主要添加了路由及用户注册和登录。

涉及到的Angular包和Meteor包:

$ meteor add angularui:angular-ui-router

$ meteor add twbs:bootstrap

$ meteor remove insecure

$ meteor add accounts-password

本课程为实验楼原创课程,转载请注明课程链接:https://www.shiyanlou.com/courses/424

Meteor+Angular实现轻论坛(1)—Meteor和Angular基础介绍 - 简书

mikel阅读(1145)

来源: Meteor+Angular实现轻论坛(1)—Meteor和Angular基础介绍 – 简书

安装 Meteor

在连了网的Linux系统上面安装Meteor是很简单的,只需要运行一条命令,大家可以去Meteor官网看看。

这里我们的虚拟机是没有联网的,所以稍微麻烦一点。

1.首先运行下面的命令下载Meteor安装包(下载非常快):

$ wget http://labfile.oss.aliyuncs.com/courses/424/meteor-install.tar.gz

2.加压刚刚下载的包:

$ tar zxvf meteor-install.tar.gz

3.进入解压后的目录:

$ cd meteor-install

4.安装:

$ sh install.sh

5.测试是否安装成功:

$ meteor --version

OK,安装完成!

Angular基础

AngularJS是一个MV* JavaScript框架,由Google维护。Angular使用声明式编程来构建用户界面,指令式编程来实现业务逻辑,达到了客户端和服务端解耦的目标。下面我们先来看看Angular的基础知识。

Angular表达式

Angular通过表达式把数据绑定到HTML模板,表达式写在双大括号内,如:{{ expression }},HTML中出现Angular表达式的地方,就会显示对应表达式的数据。来个示例看看:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Angular Demo</title>
</head>
<body>
    <div ng-app="demo" ng-init="str='hello angular'">
        <p>{{ str }}</p>
        <p>{{ 5 + 5 }}</p>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
    <script>
        var app = angular.module('demo', []);
    </script>
</body>
</html>

保存以上代码为HTML文件,在浏览器中打开,可以看到浏览器中显示了hellow angular字符串以及数字10。Angular表达式可以做简单的运算,但是不支持条件判断、循环和异常。我们还可以看到div中出现了ng-appng-init这两个奇怪的东西,这就是Angular的指令了。

Angular指令

Angular指令是HTML带有ng-前缀的扩展属性,每个指令都有不同的功能,也可以自己编写自定义指令:

  • ng-app:初始化一个Angular应用程序
  • ng-init:初始化当前作用域的属性
  • ng-model:绑定表单元素的值到当前作用域的属性
  • ng-repeat:循环HTML元素
  • ng-controller:定义应用程序的控制器

Angular控制器

Angular控制器用于控制和处理应用程序的数据。举个栗子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Angular Demo</title>
</head>
<body>
    <div ng-app="testApp" ng-controller="hello">
        <input ng-model="username">
        <p>Hello, {{ username }}</p>
    </div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script>
    // 创建Angular模块
    // 第一个参数是模块的名称
    // 第二个参数是一个数组,定义此模块需要依赖的模块(无依赖模块则传空数组)
    var app = angular.module('testApp', []);
    app.controller('hello', function($scope) {
        $scope.username = 'shiyanlou';
    });
</script>
</body>
</html>

angular.module用于创建Angular模块,通常Angular应用程序由模块定义,Angular控制器需要挂载到模块才会运行。

用浏览器打开这个页面,改变输入框的值,输入框下面的显示也会跟着改变,这个就是Angular的数据双向绑定机制。我们看到,控制器中传入了$scope这个变量,$scope相当于是这个控制器的作用域,是对当前这个控制器的数据模型(model)的引用(类似于普通JavaScript对象中的this关键字的作用)。

Meteor基础

Meteor是一个基于Node.js的Web开发框架,主要用来开发实时的应用程序。Meteor能够实时的同步数据库和客户端之间的数据。

Meteor环境搭建

安装Meteor

$ curl https://install.meteor.com/ | sh

安装完成后可以输入如下命令,查看当前安装的Meteor的版本号,以确认安装成功了:

$ meteor --version

创建示例项目

$ meteor create LouForum

这里,我们创建了一个Meteor的示例项目,进入到项目目录启动项目:

$ cd LouForum
$ meteor

启动项目的命令是在项目根目录下运行meteor,停止项目直接输入Ctrl+c即可。

启动项目中,Meteor启动了mongoDB,这是因为Meteor默认的数据库就是mongoDB。启动成功后,打开浏览器,输入http://localhost:3000即可访问此项目,这是一个Meteor的默认初始页面,可以显示鼠标点击按钮的次数。查看LouForum文件夹,我们会看到里面只有三个文件,分别是HTML文件、CSS文件和JavaScript文件。Meteor中的JavaScript代码可以同时在服务端和客户端运行,所以这里只有一个JavaScript文件。

打开LouForum.js文件,我们看到,里面有两个判断:

  • Meteor.isClient下面的代码在客户端运行
  • Meteor.isServer下面的代码在服务端运行

Meteor创建简单的app直接在项目根目录下创建js文件、HTML文件和CSS文件即可。运行项目的时候,所有的CSS文件,都会自动引入到HTML文件,而所有的HTML文件,都会拼成一个HTML文件。下面,我们去掉官方默认的模板,添加Angular进来。

添加Angular到Meteor项目

首先,我们删掉初始化项目时,自动生成的三个文件:

$ rm LouForum.html LouForum.html LouForum.html

然后创建一个index.html文件:

$ touch index.html

index.html中输入如下代码:

<body>
    <p>shiyanlou</p>
</body>

我们只写了body这个标签,而没有htmlhead标签,这是因为Meteor会扫描所有的HTML文件,然后把这些HTML文件中head标签内的内容合并,body中的内容合并,然后组成一个新的文件,再自动创建html标签、head标签和body标签,以及其他必须的代码。

运行项目:

$ meteor

启动成功后,打开浏览器访问http://localhost:3000,会看到页面上显示shiyanlou字样。

添加Angular包(在项目根目录下执行):

$ meteor add angular

添加Angular成功后,下次运行项目,前端页面会自动引用Angular,无需再手动引用。

新建一个文件:

$ touch index.ng.html

这里,我们使用了.ng.html后缀名,这样,Meteor官方默认的模板系统(Blaze)就不会编译这个类型的HTML文件,Angular就可以使用这样的HTML文件了。

index.ng.html文件中输入如下代码:

<p>shiyanlou</p>

然后修改index.html文件的代码:

<body>
    <div ng-include="'index.ng.html'"></div>
</body>

我们可以看到这里使用了Angular的一个指令,ng-include用于引入外部HTML文件,注意这里的双引号里面还有单引号。然后在创建一个app.js文件,并输入如下代码:

if (Meteor.isClient) {
    // 创建 Angular module
    // 并添加 angular-meteor 包依赖
    angular.module('louForum', ['angular-meteor']);
}

添加ng-appindex.html

<body ng-app="louForum">
    <div ng-include="index.ng.html"></div>
</body>

运行项目查看效果:

$ meteor

运行成功后,我们可以看到,浏览器显示的和刚刚一样,说明Angular创建的模块运行成功了。

项目结构组织

虽然,HTML、css和JavaScript文件直接放在项目根目录也可以运行,但是,文件多了之后就会很混乱。所以,文件组织是很有必要的。而且,特定的文件夹在Meteor项目中有特殊的含义:

  • server:此文件夹下的代码只会在服务器端运行
  • client:此文件夹下的代码只会在客户端运行
  • public:这个文件夹下面的所有文件都可以直接公开访问

这里还有一些其他的规则:

  • 子文件夹里面的文件比父文件夹里面的文件先加载
  • 同一个文件夹内,文件是按字母顺序加载的
  • 特别的,lib文件夹里的文件比其他文件夹里的文件都要先加载
  • 最后,main.*这样的文件总是最后加载

我们先创建三个文件夹,即client、server和model。client中放只需要客户端运行的代码,server放只需要服务端运行的代码,model放定义数据库collection的代码,客户端和服务端都需要运行。

然后把index.htmlindex.ng.htmlapp.js文件移动到client文件夹下面,并修改app.js代码为:

// 创建 Angular module
// 并添加 angular-meteor 包依赖
angular.module('louForum', ['angular-meteor']);

因为client文件夹下面的代码只会在客户端运行,所以不需要Meteor.isClient判断了。

index.html文件中ng-include引入的文件路径必须完整,修改index.html代码如下所示:

<body>
    <div ng-include="'client/index.ng.html'"></div>
</body>

运行项目:

$ meteor

可以看到,效果和前面一样。


本课程为实验楼原创课程,转载请注明课程链接:https://www.shiyanlou.com/courses/424