摩登前端开发笔记

Stage1

git使用技巧:在.gitconfig文件中配置git常用快件键

[alias]
ci = commit -a -v
st = status -s
br = branch
throw = reset --hard HEAD
thrown = reset --hard HEAD^

安装Node

安装nvm

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.27.1/install.sh | bash

退出 shell 重新登录,执行

nvm ls-remote

安装 Node.js

$ nvm install 5.1.1

安装完成后,查看一下

$ nvm ls

这时候可以看到自己安装的所有 Node.js 版本,输出应如下:

         v4.0.0
->       v5.0.0
         system
default -> 5.0 (-> v5.0.0)
node -> stable (-> v5.0.0) (default)
stable -> 5.0 (-> v5.0.0) (default)
iojs -> iojs- (-> N/A) (default)

完善安装

上述过程完成后,有时会出现,当开启一个新的 shell 窗口时,找不到 node 命令的情况。这种情况一般来自两个原因

1、shell 不知道 nvm 的存在

2、nvm 已经存在,但是没有 default 的 Node.js 版本可用。

解决方式:

1、检查 ~/.profile 或者 ~/.bash_profile 中有没有这样两句

export NVM_DIR="/Users/YOURUSERNAME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"  # This loads nvm

没有的话,加进去。这两句会在 bash 启动的时候被调用,然后注册 nvm 命令。

2、调用 $ nvm ls 看看像不像上述图1中一样,有 default 的指向。

如果没有的话,执行

$ nvm alias default 5.1.1

然后再

$ nvm ls

更改全局安装路径

npm get prefix #查看npm装包位置
/usr/local

默认情况下,npm 全局装包会安装到 /usr/local/ 之中,默认情况下,如果不用 sudo ,很可能会报权限错误。最好的方式是修改一下全局安装的位置. 具体步骤如下:

1、来到当前用户主目录下,新建安装位置

$ cd ~
$ mkdir .npm-global

2、把 npm 全局安装位置改为这个文件夹

$ npm config set prefix '~/.npm-global'

3、可执行文件的路径也要导出一下,到您命令行的启动加载文件中,例如我用的是bash ,所以是 ~/.bash_profile默认情况下可能是 .bashrc 或者 .profile 文件,添加下面内容:

export PATH=~/.npm-global/bin:$PATH

然后使用source命令加载一下

$ source ~/.bash_profile

接下来就可通过npm安装包了,例如全局安装gulp

$npm i -g gulp
$which gulp
/Users/peter/.npm-global/bin/gulp

安装 gulp

前提是 nodejs 已经装好。这样,先来把 gulp 全局安装一次

$ npm i -g gulp

这样的目的是为了拥有 gulp 这个系统命令。然后,新建项目,比如

$ mkdir project
$ cd project

进入项目后,还要把 gulp 在项目内安装一次

$ npm init # 生成一个 package.json 文件
$ npm i --save-dev gulp

这样做的目的是,保证项目内部的 gulpfile.js 中,使用 require('gulp') 的时候不会报错。

gulp常用API

	gulp.task('taskname', [ taskDep1, taskDep2 ],taskContentFunc)
	//定义一个任务,声明它的名称, 任务依赖, 和任务内容.

Gulp 4 add new task execution system - gulp.parallel and gulp.series

	gulp.task('taskname', gulp.series('taskDep1', gulp.parallel('taskDep2', 'taskDep3'),taskContentFunc)

Each step that can be done in concurrent will be combined in a gulp.parallel function. The others are ordered in a gulp.series function. Like that: 执行顺序

(参考文档)[https://fettblog.eu/gulp-4-parallel-and-series/]

	gulp.src( file[s] )
	//读取文件,参数为文件路径字符串或数组, 支持通配符.

	gulp.dest( destPath )
	//写入文件, 作为pipe的一个流程.文件夹不存在时会被自动创建.

	gulp.watch(files, [taskDep, taskDep2])
	//监控文件,执行任务.

进阶1:dest匹配src

gulp.dest( destPath )会将流中的文件写入到destPath中, 但并不是所有的文件都写在destPath的根目录下, 有的可能是在其下又创建子目录,其中的规则是怎样的呢~?

dest是与src相匹配的. src读取文件路径获取文件,主要有三种情况:

指定的文件:['/foo-1/bar-1.js', '/foo-1/bar-2.js','/foo-2/bar2.js'].

模糊匹配文件名的文件: ['/foo-1/bar-*.js', 'foo-2/bar-*.js'].

模糊匹配路径下的文件: [/*/bar-*.js]

以上三种情况读取的文件, 前两种会写入到destPath的根目录下, 而最后一种情况, 会在destPath下新建foo-1和foo-2文件夹然后写三个文件到相应的文件夹里. 发现规律了吗~?

dest会将src”匹配”到的文件路径写出来~(这个匹配必须是纯粹的匹配, 全匹配, 如果是foo式的半匹配就不会写文件路径) 第一和第二种匹配到的是指定文件, 第三种匹配到的是符合规则的路径下的文件, 所以会出现上面的情况. 也就是说, 如果想将源文件的路径也dest到目标路径, 那就需要将路径也放在”匹配”中.

文件匹配路径

foo.js指明特定某个文件

*.js匹配当前目录下的所有js文件,不指名扩展名则匹配所有类型

*/*.js匹配所有第一层子文件夹的js文件,第二层请用*/*/.js

**/*.js匹配所有文件夹层次下的js文件, 包括当前目录

exclude

排除的方法就是在文件匹配pattern前加!, 跟.gitignore类似. 但有些地方需要注意:如果任务需要你使用*/递归匹配, 那么执行exclude也需要递归exclude, 即!exclude/path/**/*.

这个坑是笔者在最近写atom-shell程序时遇到的: 我将atom-shell.app放在了项目目录, 需要构建成OSX的app时就把项目目录中所有的文件都拷贝到atom-shell.app下的contents/resources/app目录下. 所有文件, 当然是要用*/递归匹配喽~, 后面再跟!./atom-shell.app, 而OSX下的.app文件其实就是文件夹, 递归匹配加非递归exclude, 就造成了其实.app也被读取的状况, 然后就无限引用了… 不过gulp有自己的保护机制, 没有出死循环这样的傻事.

正确的作法是['**/*', '!./atom-shell.app', '!./atom-shell.app/**/*']. 这样就可以把app完全exclude掉了

gulp常用插件

"devDependencies": {
    "gulp": "*",                  // 基础
    "gulp-load-plugins" : "*",

    "gulp-if": "*",               // 根据不同的环境,切换方法

    "gulp-util": "*",             // 如果有自定义方法,可能会用到
    "gulp-clean": "*",            // 清理路径下文件
    "gulp-rename": "*",           // 重命名文件,比如上节提到 _ 需要还原回去
    "gulp-concat": "*",           // 文件合并

    "gulp-jshint": "*",           // jshint 检查一些格式,这个是为了统一团队的代码风格的
    "gulp-browserify": "*",       // 利用 CommonJS 的格式,直接让浏览器也能用类似的方式
    "gulp-uglify": "*",           // 替换压缩
    "gulp-cssnano"                // Minify CSS with cssnano.
		"gulp-rev"                    // Static asset revisioning by appending content hash to filenames: unicorn.css => unicorn-d41d8cd98f.css

    "gulp-htmlmin": "*",          // 压缩html代码

    "gulp-jade": "*",             // jade
    "gulp-stylus": "*",           // stylus
    "gulp-sourcemaps",            // 处理JS时,生成SourceMap

    "gulp-mocha": "*",            // 测试框架
    "chai": "*",
    "jscov": "*",
    "del",                        //Delete files/folders using globs

    "gulp-changed": "*"           // 有变化的才操作,没变化的就跳过,可进一步优化效率
  }

####gulp使用中的坑–流不兼容 使用gulp时,你可能会陷入“流不兼容”的问题。这主要是因为常规流和Vinyl文件对象有差异,或是使用了仅支持buffer(不支持流)库的gulp插件与常规流不兼容。

比如说,你不能直接将常规流与gulp和(或)gulp插件相连。我们创建一个可读流,并尝试使用gulp-uglify和gulp-rename来进行转换,将最后得到的内容交给gulp.dest()。下面就是个错误的例子:

var uglify = require('gulp-uglify'),
    rename = require('gulp-rename');
gulp.task('bundle', function() {
    return fs.createReadStream('app.js')
        .pipe(uglify())
        .pipe(rename('bundle.min.js'))
        .pipe(gulp.dest('dist/'));
});

为什么我们不能将可读流和一个gulp插件直接相连?gulp难道不就是一个基于流的构建系统吗?是的,但上面的例子忽视了一个事实,gulp插件期望的输入是Vinyl文件对象。你不能直接将一个可读流与一个以Vinyl文件对象作为输入的函数(插件)相连。

####Vinyl文件对象 gulp使用了vinyl-fs,它实现了gulp.src()和gulp.dest()方法。vinyl-fs使用vinyl文件对象——一种“虚拟文件格式”。如果我们需要将gulp和(或)gulp插件与常规的可读流一起使用,我们就需要先把可读流转换为vinyl。

使用vinyl-source-stream是个不错的选择,如下:

	var source = require('vinyl-source-stream'),
	    marked = require('gulp-marked');
	fs.createReadStream('*.md')
	    .pipe(source())
	    .pipe(marked())
	    .pipe(gulp.dest('dist/'));

另外一个例子首先通过browserify封装并最终将其转换为一个vinyl流:

	var browserify = require('browserify'),
	    uglify = require('gulp-uglify'),
	    source = require('vinyl-source-stream');
	gulp.task('bundle', function() {
	    return browserify('./src/app.js')
	        .bundle()
	        .pipe(source(‘bundle.min.js))
	        .pipe(uglify())
	        .pipe(gulp.dest('dist/'));
	});

哎呦不错哦。注意我们不再需要使用gulp-rename了,因为vinyl-source-stream创建了一个拥有指定文件名的vinyl文件实例(这样gulp.dest方法将使用这个文件名)

####流和buffer

既然你有兴趣使用gulp,这篇文章假设你已经了解了流的基础知识。无论是buffer还是流,vinyl的虚拟文件都能包含在内。使用常规可读流时,你可以监听data事件来检测数据碎片的到来:

	fs.createReadStream('/usr/share/dict/words').on('data', function(chunk) {
	    console.log('Read %d bytes of data', chunk.length);
	});
	> Read 65536 bytes of data
	> Read 65536 bytes of data
	> Read 65536 bytes of data
	> Read 65536 bytes of data
	> ...

不同的是,使用gulp.src()会将转换成buffer的vinyl文件对象重新写入到流中。也就是说,你获得的不再是数据碎片,而是将内容转换成buffer后的(虚拟)文件。vinyl文件格式拥有一个属性来表示里面是buffer还是流,gulp默认使用buffer:

	gulp.src('/usr/share/dict/words').on('data', function(file) {
	    console.log('Read %d bytes of data', file.contents.length);
	});
	> Read 2493109 bytes of data

这个例子说明了在文件被完整加入到流之前数据会被转换成buffer。

####从流到buffer

由于所需的输入(输出)流和gulp插件不尽相同,你可能需要将流转换成buffer(反之亦然)。之前已经有过介绍,大多数插件使用buffer(尽管他们的一部分也支持流)。比如gulp-uglify和gulp-traceur。你可以通过gulp-buffer来转换成buffer:

	var source = require('vinyl-source-stream'),
	    buffer = require('gulp-buffer'),
	    uglify = require('gulp-uglify');
	fs.createReadStream('./src/app.js')
	    .pipe(source('app.min.js'))
	    .pipe(buffer())
	    .pipe(uglify())
	    .pipe(gulp.dest('dist/'));

####将buffer转换为流

你也可以使用gulp-streamify或gulp-stream将一个使用buffer的插件的输出转化为一个可读流。这样处理之后,跟在使用buffer的插件后面的(只能)使用流的插件也能正常工作了。

	var wrap = require('gulp-wrap'),
	    streamify = require('gulp-streamify'),
	    uglify = require('gulp-uglify'),
	    gzip = require('gulp-gzip');
	gulp.src('app.js', {buffer: false})
	    .pipe(wrap('(function(){<%= contents %>}());'))
	    .pipe(streamify(uglify()))
	    .pipe(gulp.dest('build'))
	    .pipe(gzip())
	    .pipe(gulp.dest('build'));

###不是所有事都需要插件

虽然已经有很多使用且方便的插件,很多任务以及转换可以不使用插件而轻易完成。插件会带来一些问题,你需要依赖一个额外的npm模块,一个插件接口和(反应迟钝?)的维护者,等等。如果一个任务可以不使用插件而使用原生模块就能轻易完成,绝大多数情况下,都建议不要使用插件。能够理解上面所说的概念,并能够在所处的情况下做出正确的决定,这点非常重要。

####安装sass

到项目文件夹内,然后输入:

npm i --save-dev gulp-sass

注意,gulp-sass 依赖于 libsass ,这是一个 C++ 的包,需要在本地编译,所以要确保本地 Mac 机器上是有 Xcode 的。 装好之后,gulpfile.js 中写下面的内容

var gulp = require('gulp');
var sass = require('gulp-sass');

gulp.task('sass', function(){
  gulp.src('main.scss')
      .pipe(sass())
      .pipe(gulp.dest('css'));
});

####强大的 gulp 管道线

比如,还可以扩展添加 gulp-autoprefixer 进来

$ npm i gulp-autoprefixer -D

然后到gulpfile.js中添加

var prefix = require('gulp-autoprefixer');
    .pipe(prefix())

这样,在输出的 main.css 中,就可以看到 vendor prefix 已经自动添加了。

####gulp watch

var gulp = require('gulp');
var sass = require('gulp-sass');
var prefix = require('gulp-autoprefixer');

gulp.task('sass', function(){
  gulp.src('styles/main.scss')
      .pipe(sass())
      .pipe(prefix())
      .pipe(gulp.dest('css'));
});

gulp.task('watch', function(){
  gulp.watch('styles/*.scss', ['sass']);
});

####定制自己的色盘 google material 调色盘

####主色

主色要至少占据页面 30% 的区域,可以配合黑白灰,共同构成页面的大背景。

####强调色

用来凸显一些重要操作或内容。

####dark/light 背景色

700 的深色背景可以用来做状态栏,突出一些比较重要的操作,300 的浅色区域可以用来放次要信息。参考:Google 官方的 Material 色盘使用说明 。

####主从文字颜色

要体现内容层次,一方面可以用字体大小,但是如果配上主从字体颜色,效果会更好。 关于颜色使用,官方是给了 spec 的。 用到的字体设置:

font-family: "Helvetica Neue", "Segoe UI", Helvetica, Arial, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;

###stage2

对项目进行组件化重构,突出一个概念:组件化。会用到 react 和 webpack 。

####资源 国内淘宝npm镜像

####React

####安装 npm i install -g ####在项目中引用react

var React = require('react');
var Books = React.createClass({
	render:function(){
		return(
			 <div className="books clearfix">
		        <div className="book"><img src="images/github.jpg" alt className="cover" /></div>
		        <div className="book"><img src="images/tealeaf-http.jpg" alt className="cover" /></div>
		        <div className="book"><img src="images/tlcl-book.jpg" alt className="cover" /></div>
		     </div>
		)
	}
});
module.exports = Books; //将Books封装成node模块,以便外部调用;

####webpack

webpack 官网 ,可以看到介绍很简单,是一个 module bundler (不管有多少个输入文件,输出只有一个bundle.js)。

  • 提供原生js不支持的require()功能。
  • 提供loader。 #####安装

    npm install webpack -g

#####配置webpack.config.js

module.exports = {
    entry: "./entry.js",
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
        	//compile jsx
            { test: /\.js$/, loader: "babel" },
            //compile sass
            { test: /\.scss$/, loader: "style!css!autoprefixer!sass" }
        ]
    }
};

创建入口文件entry.js

var React = require('react');
var App = require('./components/App.js');
require('./styles/main.scss');
React.render(<App /> , document.getElementById('app'));

然后再命令行中输入webpack命令

$ wbpack

#####常用loader

  • bable-loader 用来compile jsx文件;
  • style-loader
  • css-loader
  • sass-loader
  • autoprefixer-loader

#####监视sass文件变化 webpack –watch