企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 简介 使用 gulp 或者 npm 的方式都可以让我们执行一些自动化的前端构建任务。 当然使用`npm run`的方式会更灵活,我们可以自己编写脚本来满足需求。 使用gulp 插件的方式则省事不少,而且插件丰富,本片目的就是为了实现**基于gulp的多页应用打包**。 本篇文章介绍的比较简单,主要是对于 website 的前端开发构建流程。对于移动web APP的构建可以查看 yeoman 。或者以后在进行总结。 # Gulp 流 [探究Gulp的Stream](https://segmentfault.com/a/1190000003770541) 虽然Gulp使用的是`Stream`,但却不是普通的`Node Stream`,实际上,Gulp(以及Gulp插件)用的应该叫做`Vinyl File Object Stream`。 这里的Vinyl,是一种虚拟文件格式。Vinyl主要用两个属性来描述文件,它们分别是路径(path)及内容(contents)。具体来说,Vinyl并不神秘,它仍然是JavaScript Object。Vinyl官方给了这样的示例: ```js var File = require('vinyl'); var coffeeFile = new File({ cwd: "/", base: "/test/", path: "/test/file.coffee", contents: new Buffer("test = 123") }); ``` 从这段代码可以看出,Vinyl是Object,path和contents也正是这个Object的属性。 # 构建流程详解: 1. 在 **开发环境(development环境)** 下:需要默认将 SRC 下的文件复制到 WWW 目录下,并且实时监听 SRC 文件的改动映射到 www(因为 ionic serve 默认会读取 www 目录下的文件启动本地服务);开发模式主要的 gulp 任务是监听 html, css, JS 的改动,sass 的编译,实时刷新,stylelint,JSLint,文件替换和 API 路径配置; 2. 在 **预生产环境(staging环境)** 下:首先需要清除掉 WWW 目录下的所有文件,然后重新从 SRC 编译代码到WWW 目录下,这个过程主要的 gulp 任务是:清除文件,HTML 模板合并缓存,图片压缩,CSS 合并压缩,JS 合并压缩; 3. 这一步最好,还是进行重启服务器进行最后一步的测试。 4. 在 **生产环境(production环境)** 下: 生产模式和预生产模式除了 API PATH 配置不一样,其它的gulp任务都是一样; # 模块划分 模块的定义需要根据项目而定,切分的粒度也需要视情况而定,例如**我们可以将 login切换为一个模块,所有有关login模块的html,css,js,img都放在login模块目录下**: 推荐阅读: [前端工程——基础篇](https://github.com/fouber/blog/issues/10) # 目录结构 目录树: ~~~ / | |- src/ | | | | |- index/ | | |- home/ | | | |- home.controller.js | | | |- home.html | | | |- home.scss | | | | | | |- login/ | | | |- login.controller.js | | | |- login.html | | | |- login.scss | | | | | | |- config/ | | | |- config.default.json | | | |- config.development.json | | | |- config.production.json | | | |- config.js | | | | | |- ... | | | | |- styles/ | | |- scss/ | | | |- ionic.app.scss (index file) | | |- ionic.app.css (all css will be compiled into this file) | | | | |- images/ | | | | |- lib/ | | |- angular/ | | |- angular-resource | | |- ionic | |- shared/ (common components cross projects) | | |- canvasClock/ | | |- calendar/ | | |- constants/ | |- index.html | |- app ~~~ 初始化项目目录 ```shell $ mkdir ES6-with-gulp-babe && cd ES6-with-gulp-babe && git init && npm init ``` 然后自己进行项目的说明配置。 确定项目目录: ```js var paths = { src : './src', //开发目录 dest : './dist' // 生产的目录 (该目录视情况而定,比如ionic项目就必须是名为www目录) }; ``` 然后在`gulp.js`中,使用就行。 在项目部署时,只会针对`dest`目录,js和css、图片文件会被全部放置到`./dist/assets`目录下。 # 环境的区分 通过传入env的参数区分: ~~~ $ gulp build --env development/staging/production ~~~ 开发环境,预发布环境,还是部署环境。 # 项目版权/注释 https://github.com/tracker1/gulp-header ```js var header = require('gulp-header'), //... var pkg = require('./package.json'); var banner = [ '/*!\n' + ' * <%= pkg.name %>\n' + ' * <%= pkg.title %>\n' + ' * <%= pkg.url %>\n' + ' * @author <%= _.capitalize(pkg.author) %>\n' + ' * @version <%= pkg.version %>\n' + ' * Copyright ' + new Date().getFullYear() + '. <%= pkg.license %> licensed.\n' + ' */', '\n' ].join(''); ``` # 前端静态服务器 ## 要求 * **跨域转发** 受到浏览器内部机制的限制,本域内,也就是自身服务器下的服务的js是不能操作其他域下的页面对象,或者去请求域下的数据,这个时候就会产生跨域问题。 为什么cdn或者图片加载的时候,不是本域的东西,却可以请求的来?跨域是指js不能操作和请求其他域的,浏览器本身请求和标签src内部的请求是不受限制的。 * **实时刷新的静态服务器** [browser-sync](https://browsersync.io/) 启动 http静态服务器环境。当然还有其他的很多工具都可以使用。 * **热更新** [拥有实时重载(live-reloading)和 CSS 注入的服务器](http://www.gulpjs.com.cn/docs/recipes/server-with-livereload-and-css-injection/) * **模拟后端API的数据-前端的mock server**(//TODO) Web开发是一个前后端合作的工作,但在开发的前期,双方约定好接口和数据后,就进入了各自的开发,这时候前端就需要有自己的开发环境,能够与最后和后端联调时后端提供的服务器能力接近,并且能够简化前端的工作流程,自动化完成相关任务。 对于项目中所有的ajax请求进行拦截,返回配置的数据,虚拟一个服务器,模拟后台返回json数据给前端,**这样可以一定程度上实现前后端分离,约定好接口之后,前后端即可同时开发,从而提高效率。** ## TODO 模拟后端API的数据-前端的mock server(//TODO) https://nodemon.io/ https://github.com/JacksonGariety/gulp-nodemon https://github.com/colynb/gulp-data ## 方案 某个项目我们用到了json的方式传递数据,因api接口的域名和实际本地开发的域名不相同,所以有跨域的情况出现 项目本地开发域名: `dms.dev-adtime.com` 项目服务器接口api的域名: `api.dev.adt100.net` 要将 `api.dev.adt100.net/dmc` 都转成 `dms.dev-adtime.com/dmc` ~~原来的做法:nginx 设置反向代理~~ 现在的做法: 1. `browser-sync` 启动 http静态服务器环境 2. `http-proxy-middleware` 设置反向代理 安装: ```shell $ $ npm i -D browser-sync http-proxy-middleware ``` 配置 ```js var gulp = require('gulp'); // 主要 var browserSync = require('browser-sync'); var proxyMiddleware = require('http-proxy-middleware'); // http环境,接口反向代理 gulp.task('browser-sync', function() { // 多个地址的反向代理 var proxy163 = proxyMiddleware('/f2e', { target: 'http://img1.cache.netease.com', headers: { host:'img1.cache.netease.com' // 这个挺关键 } }); var proxyAdtime = proxyMiddleware('/apps', { target: 'http://cdn.adt100.com', headers: { host:'cdn.adt100.com' } }); browserSync({ server: { baseDir: "./", port: 80, middleware: [proxy163, proxyAdtime] } }); }); ``` ## 参考 [GulpJs入门与实践](https://libraries.io/github/hellosun/gulpDemo) [为browser-sync设置代理](http://yangblink.com/2016/09/17/为browser-sync设置代理/) # css 操作 我要进行一个工程的样式文件进行发布,那我要先对scss进行编译,然后对里面的图片合并为雪碧图,最后在对处理好的css进行合并压缩,生成map。 ## 要求 1. style-lint stylelint 由 PostCSS 提供技术支持,所以它也可以理解 PostCSS 解析的语法,比如 SCSS。 [使用stylelint对CSS/Sass做代码审查](http://www.w3cplus.com/workflow/How-to-lint-your-css-with-stylelint.html) 2. css压缩 以下几种任选一种: [gulp-csso](https://github.com/ben-eb/gulp-csso) [gulp-cssnano](https://www.npmjs.com/package/gulp-cssnano) //主要使用postcss生态 [shorthand](https://github.com/vol7/shorthand) 3. [使用gulp工具让px自主转化为rem](http://www.yaya12.com/archives/710)。 使用插件:注意下列代码的75代表了1rem对应的px值,这个值根据设计师提供的设计图的总宽度/10决定。如果设计图的总宽度是以750px为标准,则填写75;如果是375px,则填写37.5;以此类推…… ```js var gulp = require('gulp'); var postcss = require('gulp-postcss'); var px2rem = require('postcss-px2rem'); gulp.task('default', function() { var processors = [px2rem({ remUnit: 75 })]; return gulp.src('./src/*.css') .pipe(postcss(processors)) .pipe(gulp.dest('./dest')); }); ``` 4. css spirit 任选一种: [gulp.spritesmith](https://www.npmjs.com/package/gulp.spritesmith) [gulp-tmtsprite](https://github.com/weixin/gulp-tmtsprite) 5. 可以考虑配合添加版本号更新css中的image ```js gulp.task('replaceImgInCss', function () { //css,主要是针对css 中的img替换 gulp.src(['rev/**/rev-img-manifest.json', buildBasePath + 'css/*.css']) .pipe(plumber()) .pipe(revCollector({ replaceReved: true })) .pipe(gulp.dest(config.dest + config.cssDir)); }); ``` ## 方案 可以考虑使用使用Sass(和Compass)编写CSS。就是Compass好久没更新了! 使用`gulp-autoprefixer`根据设置浏览器版本自动处理浏览器前缀。 ```js var autoPrefixBrowserList = ['last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4']; autoprefixer({ browsers: autoPrefixBrowserList, cascade: true }) ``` `gulp-autoprefixer`的browsers参数详解 (传送门): ● `last 2 versions`: 主流浏览器的最新两个版本 ● `last 1 Chrome versions`: 谷歌浏览器的最新版本 ● `last 2 Explorer versions`: IE的最新两个版本 ● `last 3 Safari versions`: 苹果浏览器最新三个版本 ● `Firefox >= 20`: 火狐浏览器的版本大于或等于20 ● `iOS 7`: IOS7版本 ● `Firefox ESR`: 最新ESR版本的火狐 ● `> 5%`: 全球统计有超过5%的使用率 # js 操作 ## 要求 1. [ESLint](http://zhenhua-lee.github.io/tools/linter.html) 2. 压缩-ugly/packer,合并。 压缩的顺序不能出错 3. source-map 4. require.js 需要`r.js`进行打包,可选[gulp-requirejs-optimize](https://www.npmjs.com/package/gulp-requirejs-optimize)。 5. 前端资源缓存问题-md5 [gulp-rev](https://www.npmjs.com/package/gulp-rev) 用来为文件加上增加散列值 [gulp-rev-collector](https://github.com/shonny-ua/gulp-rev-collector) 配合gulp-rev用来替换html文件中的文件名 [gulp-rev和gulp-rev-collector的使用](https://www.manster.me/?p=488) [前端静态资源版本更新与缓存——通过gulp 在原html文件上自动化添加js、css、img版本号](http://www.jianshu.com/p/164344f8e2ac) 静态服务器要配置静态资源的过期时间为永不过期。 这样做的目的是: 静态资源在客户端只会请求一次(首次),永久缓存,之后访问不会再发送请求协商304 更改、更新会指定到对应的文件 不删除旧版本(cdn等),以便回滚代码,回滚的时候更新html,同时也不会增加http请求。 6. 如果项目使用了typescript [gulp-typescript](https://github.com/ivogabe/gulp-typescript) - TypeScript in JavaScript. 7. combo 线上 ## 方案 如果web前端模块化了requireJS,就需要`r.js`进行打包,可选[gulp-requirejs-optimize](https://www.npmjs.com/package/gulp-requirejs-optimize)。 ```js // 操作r.js打包任务 var rjs = function (){ return gulp.src(config.rbuildjs) //只打包指定目录下requirejs模块化用到的js文件(这样就不容易打包出错了) .pipe(rjsOptimize({ name: 'main',// 模块入口 out: config.src+"./main-built.js", // 输出压缩后的文件位置 mainConfigFile: config.rbuildjsPath, // exclude: [ // 'jquery' // ] })) .pipe(gulp.dest(config.dest)); //最后的目录在`config.dest+out`下面..哈哈 */ } ``` # images 操作 ## 要求 1. 图片压缩 [gulp-imagemin](http://www.ydcss.com/archives/26) 2. ## 使用工具 ## 代码 ## 方案 ## 参考 # html 操作 ## 要求 1. html模块化,打包时,使用插件include 各个html的模块部分 类似这样的结构: ~~~ src |--- ccomponent |--- tabbox |--- main.js |--- main.html |--- main.css |--- img //图片目录 |--- header |--- header.js |--- header.html |--- header.css |--- img //图片目录 ~~~ google:html gulp include模块 1. 使用posthtml 2. [gulp-content-includer](https://github.com/hellopao/gulp_plugin/tree/master/gulp-content-includer) 2. html页面压缩 [gulp-htmlmin](https://github.com/jonschlinkert/gulp-htmlmin) ```js .pipe(htmlmin({ collapseWhitespace: true, removeComments: true })) ``` 同时页面中的内联的css和js也要压缩 3. html中引用的 css 和 js 自动合并 [gulp-merge-link](https://github.com/aDaiCode/gulp-merge-link)-合并html页面中的js或css ## 方案 使用`gulp-inject` ```shell npm install --save-dev gulp-inject ``` # 资源导出目录 资源导出目录dist为文件资源导出目录,需要配合启动服务器进行处理分为两个文件夹,images和pages;images仅存放通用的图片资源。pages目录结构和资源目录一致 # 代码的编写路径问题 根据代码结构,建议在编写代码的过程中页面独有资源采用相对路径,获取公用资源建议采用绝对路径,绝对路径根目录为dist目录 # Gulp插件 可以使用common.js的方式将gulp.js进行模块分割。 需要**同步执行任务**。([run-sequence](https://github.com/OverZealous/run-sequence),gulp 4.0自带了同步执行函数) [1. gulp-load-plugins](https://github.com/jackfranklin/gulp-load-plugins) 不用在一个一个写插件了。 2. gulp-sass 用于将 Sass 文件编译为 CSS 文件 3. gulp-autoprefixer 根据浏览器版本自动处理添加浏览器前缀 4. browser-sync 能让浏览器实时、快速响应文件更改(html、js、css、sass、less等)并自动刷新页面 5. gulp-notify 用于任务提醒 6. [gulp-plumber](https://github.com/floatdrop/gulp-plumber) 正确的错误处理应该是能遇到错误时输出错误,并且能够保证整个gulp进程不挂,而且功能还能正常用。https://kejyuntw.gitbooks.io/gulp-learning-notes/plguins/Tool/Plugins-Tool-gulp-plumber.html **注意**:pipe(gulpPlumber())一定要在编译Scss及最小化JavaScript之前就优先加入(加入顺序有差异),否则会无法起到作用 7. [gulp-sourcemaps](https://github.com/gulp-sourcemaps/gulp-sourcemaps) 生成source map,方便跟踪问题代码 8. [gulp-util](https://github.com/gulpjs/gulp-util) gulp工具集 9. [gulp-cssnano](https://github.com/ben-eb/gulp-cssnano) 压缩css文件 10. browserify 让你使用类似于 node 的 require() 的方式来组织浏览器端的 Javascript 代码 11. [vinyl-source-stream](https://github.com/hughsk/vinyl-source-stream) 将Browserify的bundle()的输出转换为Gulp可用的vinyl(一种虚拟文件格式)流。 12. [gulp-if](https://github.com/robrich/gulp-if),支持条件判断(支持函数条件)来控制相关流程。 13. [gulp-filter](https://github.com/sindresorhus/gulp-filter) 支持glob模式、函数过滤,以及过滤后恢复原来的输入 14. gulp-notify 当gulp 处理完档案后,可以使用gulp-notify 告知我们处理状况。 # 当中可能需要会用到的文件 ## [Modernizr.js](https://modernizr.com/) Modernizr是基于渐进增强理论来开发的,所以它支持并鼓励开发者一层一层的创建他们的网站。一切从一个应用了Javascript的空闲地基开始,一个接一个的添加增强的应用层。因为使用了Modernizr,所以你容易知道浏览器都支持什么。 它会检测当前浏览器是否支持CSS3的特性,比如`@font-face`、`border-radius`、 `border-image`、`box-shadow`、`rgba()` 等,同时也会检测是否支持HTML5的特性——比如`audio`、`video`、本地储存、和新的 `<input>`标签的类型和属性等。 内部可以自定义的包含需要检测的html5特性和[html5shiv.js](https://github.com/aFarkas/html5shiv) ## [Selectivizr.js](http://selectivizr.com/) Selectivizr是一个JS文件,你只需要在使用前先加载下列框架中的任何一个:JQuery、dojo、prototype、Yahoo YUI、DOMAssistant、mootools、NVMatcher,然后再调用Selectivizr,就可以让IE6/7/8支持CSS3选择器。 ## [Respond.js](https://github.com/scottjehl/Respond) ### 插件原理 1. 接下来,需要理解`respond.js`的实现思路: 2. 第一步,将`head`中所有外部引入的`CSS`文件路径取出来存储到一个数组当中; 3. 第二步,遍历数组,并一个个发送`AJAX`请求; 4. 第三步,`AJAX`回调后,分析`response`中的`media query`的`min-width`和`max-width`语法(注意,仅仅支持`min-width`和`max-width`),分析出`viewport`变化区间对应相应的`css`块; 5. 第四步,页面初始化时和`window.resize`时,根据当前`viewport`使用相应的`css`块。 > [HTML5 respond.js 解决IE6~8的响应式布局问题](http://blog.163.com/hongshaoguoguo@126/blog/static/18046981201410745621487/) # 代码风格 ```html <script type="text/javascript" src="//use.typekit.net/mkq4pib.js"></script> ``` `//`表示同协议,一般现在用在`https`跨域名地址情况下。比如**第三方统计代码的引入**,用`//`就不用很麻烦地区分`https`还是`http`,也不用担心`https`下降到http出问题 # 参考 [Web Starter Kit ](https://github.com/google/web-starter-kit/) [【使用gulp解决RequireJS项目前端缓存问题(一)】的更多相关文章](https://www.bbsmax.com/R/Ae5R1oAJQ9/) [H-ui 前端框架](http://www.h-ui.net/index.shtml) https://github.com/wungqiang/requirejs-skeleton [frontend-boilerplate](https://github.com/leonardofaria/frontend-boilerplate) [runkit](https://npm.runkit.com/generator-ionic-gulp-compass) [ES6-with-gulp-build](https://github.com/chenbin92/ES6-with-gulp-build/issues/6) [gulp-sass-bourbon-neat-browsersync-boilerplate](http://meredithunderell.com/gulp-sass-bourbon-neat-browsersync-boilerplate/)