一、前端工程化

概念:遵循一定的标准和规范,通过工具去提高效率,降低成本的一种手段

1、遇到的主要问题

想要使用ES6+新特性,但是兼容有问题
想要使用Less/Sass/PostCSS增强CSS的编程性,但是运行环境不能直接支持
想要使用模块化的方式提高项目的可维护性,但运行环境不能直接支持
部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器
多人协同开发,无法硬性统一大家的代码风格,从仓库中pull回来的代码质量无法保证

2、主要解决的问题

传统语言或语法的弊端,无法使用模块化/组件化,重复的机械式工作,代码风格统一、质量保证,依赖后端服务接口支持,整体依赖后端项目

3、工程化的表现

一切提高效率、降低成本、质量保证为目的的收到都属于 工程化

创建项目
       创建项目结构
        创建特定类型文件
编码
        格式化代码
        校验代码风格
        编译/构建/打包
预览/测试
        + Web Server / Mock
        Live Reloading / HMR
        Source Map
提交
        Git Hooks
        Lint-staged
        持续集成
部署
        CI / CD
        自动发布

4、工程化不等于某个具体工具

工具并不是工程化的核心,工程化的核心是对项目的整体规划或架构,工具只是落地和实现工程化的一个手段

一些成熟的工程化集成:

  • create-react-app
  • vue-cli
  • angular-cli
  • gatsby-cli

上面的几个是某个项目的官方提供的集成化方案

5、工程化与Node.js

工程化工具都是Node.js开发的

二、脚手架工具

脚手架的本质作用:创建项目基础结构、提供项目规范和约定

1、脚手架工具的作用

包括:相同的组织结构,相同的开发范式,相同的模块依赖,相同的工具配置,相同的基础代码

脚手架就是解决上面问题的工具,通过创建项目骨架自动的执行工作。IDE创建项目的过程就是一个脚手架的工作流程。

由于前端技术选型比较多样,又没有一个统一的标准,所以前端脚手架不会集成在某一个IDE中,一般都是以一个独立的工具存在,相对会复杂一些。

2、 常用的脚手架工具

React项目:create-react-app

Vue.js项目:vue-cli

Angular项目:angular-cli

根据信息创建对应的项目基础结构

Yeoman脚手架工具、plop

3、通用脚手架工具剖析

Yeoman是最老牌、最强大、最通用的脚手架工具,是创建现代化应用的脚手架工具,不同于vue-cli,Yeoman更像是脚手架运行平台,我们可以通过Yeoman搭配不同的Generator去创建任何类型的项目,我们可以创建我们自己的Generator,从而去创建我们自己的前端脚手架。缺点是,在框架开发的项目中,Yeoman过于通用不够专注。

Yeoman 安装

(2)SubGenerator

有时候我们可能不需要创建一个完成的项目结构,而是在已有项目的基础上,创建一些项目文件,如README.md,或者是创建一些特定类型的文件,如ESLint、Babel配置文件

  • 运行SubGenerator的方式就是在原有Generator基础上加上:SubGenerator的名字,如:yo node:cli
  • 在使用SubGenerator前,要先去查看一下Generator之下有哪些SubGenerato

(3)Plop

Plop是一个小而美的脚手架工具,通常用于创建项目中特定类型文件的小工具,一般是把Plop集成到项目中,用来自动化创建同类型的项目文件。

如何使用Plop创建文件:

  • 将plop模块作为项目开发依赖安装
  • 在项目根目录下创建一个plopfile.js文件
  • 在plopfile.js文件中定义脚手架任务
  • 编写用于生成特定类型文件的模板
  • 通过Plop提供的cli运行脚手架任务

4、 脚手架工作原理

脚手架的工作原理就是在启动脚手架之后,回自动地去询问一些预设问题,通过回答的结果结合一些模板文件,生成项目的结构。

使用NodeJS开发一个小型的脚手架工具:

  • 用npm init初始化一个空文件夹:sample-scaffolding

  • package.json中添加bin属性指定脚手架的命令入口文件为cli.js

    {"name": "sample-scaffolding","version": "1.0.0","main": "index.js","bin": "cli.js","license": "MIT","dependencies": {"ejs": "^3.1.3","inquirer": "^7.1.0"}
    }
  • 编写cli.js

Npm install inquirear   node中发起命令行交互询问使用inquirear 模块

Npm install ejs        ejs 模板引擎

#!/usr/bin/env node// Node CLI 应用入口文件必须要有这样的文件头
// 如果Linux 或者 Mac 系统下,还需要修改此文件权限为755: chmod 755 cli.js// 脚手架工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的结果生成文件const path = require('path')
const fs = require('fs')
const inquirer = require('inquirer') // 发起命令行交互询问
const ejs = require('ejs') // 模板引擎
inquirer.prompt([{type: 'input',name: 'name',message: 'Project name?'}
]).then(answer => {console.log(answer)// 模板目录const tempDir = path.join(__dirname, 'templates')// 目标目录const destDir = process.cwd()// 将模板下的文件全部转换到目标目录fs.readdir(tempDir, (err, files) => {if (err) throw errfiles.forEach(file => {// 通过模板引擎渲染文件ejs.renderFile(path.join(tempDir, file), answer, (err, result) => {if(err) throw err// 将结果写入到目标目录fs.writeFileSync(path.join(destDir, file), result)})})})
})
  • 将该cli程序link到全局:npm link
  • 然后再其他文件夹中执行:sample-scaffolding命令,就可以根据模板自动化创建文件了。

5、 自定义Generator开发脚手架

注意:Yeoman的生成器名称必须是generator-<name>,安装生成器的时候,就执行yo <name>

创建Generator生成器的步骤:

  • mkdir generator-sample

  • cd generator-sample

  • npm init

  • npm install yeoman-generator

  • 创建文件:generators/app/index.jsx

// 此文件作为Generator的核心入口
// 需要导出一个集成字Yeoman Generator的类型
// Yeoman Generator在工作时会自动调用我们在此类型中定义的一些生命周期方法
// 我们在这些方法中可以通过调用父类提供的一些工具方法实现一些功能,比如文件写入const Generator = require('yeoman-generator')module.exports = class extends Generator {prompting () {// Yeoman 在询问用户环节会自动调用次方法// 在此方法中可以调用父类的prompt()方法发出对用户命令行询问return this.prompt([{type: 'input',name: 'name',message: 'Your project name',default: this.appname // appname为项目生成目录}]).then( answers => {// answers => {name: 'user input value'}this.answers = answers})}writing () {// Yeoman 自动在生成文件阶段调用次方法// 我们这里尝试往项目目录中写入文件// this.fs.write(//   this.destinationPath('temp.txt'),//   Math.random().toString()// )// 通过模板方法导入文件到目标目录// 模板文件路径const tmpl = this.templatePath('foo.txt')// 输出目标路径const output = this.destinationPath('foo.txt')// 模板数组上下文const context = {title: 'Hello', success: false}// const context = this.answers // 从命令行获取的参数this.fs.copyTpl(tmpl, output, context)}
}
  • templates/foo.txt作为模板文件
这是一个模板文件
内部可以使用EJS模板标记输出数据例如:<%= title %>其他的EJS语法也支持<%if (success) {%>hello world
<%}%>

执行npm link, 此时这个模块就会作为全局模块被link到全局,别的项目可以直接使用它。

创建一个别的文件夹my-proj, 在这个文件夹中执行:yo sample

发布到npmjs网站上:npm publish --registry=镜像源

6、 Plop

npm install plop
plopfile.js

// Plop 入口文件,需要导入一个函数
// 此函数接受一个plop对象,用户创建生成器任务module.exports = plop => {plop.setGenerator('component', {description: 'create a component',prompts: [{type: 'input',name: 'name',message: 'component name',default: 'MyComponent'}],actions: [{type: 'add', // 代表添加文件path: 'src/components/{{name}}/{{name}}.js',templateFile: 'plop-templates/component.hbs'},{type: 'add', // 代表添加文件path: 'src/components/{{name}}/{{name}}.css',templateFile: 'plop-templates/component.css.hbs'},]})
}

编写模板:

component.hbs:

import React from 'react';export default () => (<div className="{{name}}"><h1>{{name}} Component</h1></div>
)

Component.css.hbs:

import React from 'react';
import ReactDOM from 'react-dom';
import {{name}} from './{{name}}';it('renders without crashing', () => {const div = documents.createElement('div');ReactDOM.render(<{{name}}/>, div);ReactDOM.unmountComponentAtNode(div)
})

三、自动化构建

源代码自动化构建成生产代码,也称为自动化构建工作流。

使用提高效率的语法、规范和标准,如:ECMAScript Next、Sass、模板引擎,这些用法大都不被浏览器直接支持,自动化工具就是解决这些问题的,构建转换那些不被支持的特性。

1、 NPM Scripts

在package.json中增加一个scripts对象,如:

{"scripts": {"build": "sass scss/main.scss css/style.css"}
}

scripts可以自动发现node_modules里面的命令,所以不需要写完整的路径,直接写命令的名称就可以。然后可以通过npm或yarn运行scripts下面的命令名称,npm用run启动,yarn可以省略run,如:npm run buildyarn build

NPM Scripts是实现自动化构建工作流的最简方式。

{"scripts": {"build": "sass scss/main.scss css/style.css","preserve": "yarn build","serve": "browser-sync ."}
}

preserve是一个钩子,保证在执行serve之前,会先执行build,使样式先处理,然后再执行serve。

通过--watch可以监听sass文件的变化自动编译,但是此时sass命令在工作时,命令行会阻塞,去等待文件的变化,导致了后面的serve无法去工作,此时就需要同时去执行多个任务,要安装npm-run-all这个模块

{"scripts": {"build": "sass scss/main.scss css/style.css --watch","serve": "browser-sync .","start": "run-p build serve"}
}

运行npm run start命令,build和serve就会被同时执行。

2、Grunt

Grunt是最早的前端构建系统,它的插件生态非常完善,它的插件可以帮你完成任何你想做的事情。由于Grunt工作过程是基于临时文件去实现的,所以会比较慢。

如何使用Grunt:

  • 安装grunt:npm install grunt,编写gruntfile.js文件,下面举例grunt任务的几种用法:
// Grunt的入口文件
// 用于定义一些需要Grunt自动执行的任务
// 需要导出一个函数
// 此函数接受一个grunt的形参,内部提供一些创建任务时可以用到的APImodule.exports = grunt => {grunt.registerTask('foo', () => {// 第一个参数是任务名字,第二个参数接受一个回调函数,是指定任务的执行内容,执行命令是yarn grunt fooconsole.log('hello grunt ~')})grunt.registerTask('bar', '任务描述', () => { // 如果第二个参数是字符串,则是任务描述,执行命令是yarn grunt barconsole.log('other task~')})grunt.registerTask('default', () => { // 如果任务名称是'default',则为默认任务,grunt在运行时不需要执行任务名称,自动执行默认任务,执行命令是yarn gruntconsole.log('default task')})grunt.registerTask('default', ['foo', 'bad', 'bar']) // 一般用default映射其他任务,第二个参数传入一个数组,数组中指定任务的名字,grunt执行默认任务,则会依次执行数组中的任务,执行命令是yarn grunt// grunt.registerTask('async-task', () => {//   setTimeout(() => {//     console.log('async task working')//   }, 1000);// })// 异步任务,done()表示结束grunt.registerTask('async-task', function () { // grunt代码默认支持同步模式,如果需要异步操作,则需要通过this.async()得到一个回调函数,在你的异步操作完成过后,去调用这个回调函数,标记这个任务已经被完成。知道done()被执行,grunt才会结束这个任务的执行。执行命令是yarn grunt async-taskconst done = this.async()setTimeout(() => {console.log('async task working..')done()}, 1000);})// 失败任务grunt.registerTask('bad', () => { // 通过return false标志这个任务执行失败,执行命令是yarn grunt bad。如果是在任务列表中,这个任务的失败会导致后序所有任务不再被执行,执行命令是yarn grunt。可以通过--force参数强制执行所有的任务,,执行命令是yarn grunt default --forceconsole.log('bad working...')return false})// 异步失败任务,done(false)表示任务失败,执行命令是yarn grunt bad-async-taskgrunt.registerTask('bad-async-task', function () {const done = this.async()setTimeout(() => {console.log('bad async task working..')done(false)}, 1000);})
}

grunt配置选项

module.exports = grunt => {grunt.initConfig({// 对象的属性名一般与任务名保持一致。// foo: 'bar'foo: {bar: 123}})grunt.registerTask('foo', () => {// console.log(grunt.config('foo')) // barconsole.log(grunt.config('foo.bar')) // 123.grunt的config支持通过foo.bar的形式获取属性值,也可以通过获取foo对象,然后取属性})
}
  • 多目标任务(相当于子任务)
module.exports = grunt => {grunt.initConfig({// 与任务名称同名build: {options: { // 是配置选项,不会作为任务foo: 'bar'},// 每一个对象属性都是一个任务css: {options: { // 会覆盖上层的optionsfoo: 'baz'}},// 每一个对象属性都是一个任务js: '2'}})// 多目标任务,可以让任务根据配置形成多个子任务,registerMultiTask方法,第一个参数是任务名,第二个参数是任务的回调函数grunt.registerMultiTask('build', function () {console.log(this.options())console.log(`build task: ${this.target}, data: ${this.data}`)})

执行命令:grunt build, 输出结果:

Running "build:css" (build) task{ foo: 'baz' }build task: css, data: [object Object]Running "build:js" (build) task{ foo: 'bar' }build task: js, data: 2
  • grunt插件使用

插件机制是grunt的核心,因为很多构建任务都是通用的,社区当中也就出现了很多通用的插件,这些插件中封装了很多通用的任务,一般情况下我们的构建过程都是由通用的构建任务组成的。先去npm中安装 需要的插件,再去gruntfile中使用grunt.loadNpmTasks方法载入这个插件,最后根据插件的文档完成相关的配置选项。

例如使用clean插件,安装 yarn add grunt-contrib-clean,用来清除临时文件。

module.exports = grunt => {
// 多目标任务需要通过initConfig配置目标grunt.initConfig({clean: {temp: 'temp/**' // ** 表示temp下的子目录以及子目录下的文件}})grunt.loadNpmTasks('grunt-contrib-clean')
}

执行:grunt clean ,就会删除temp文件夹

  • Grunt常用插件总结:

    • grunt-sass
    • grunt-babel
    • grunt-watch
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {grunt.initConfig({sass: {options: {sourceMap: true,implementation: sass, // implementation指定在grunt-sass中使用哪个模块对sass进行编译,我们使用npm中的sass},main: {files: {'dist/css/main.css': 'src/scss/main.scss'}}},babel: {options: {presets: ['@babel/preset-env'],sourceMap: true},main: {files: {'dist/js/app.js': 'src/js/app.js'}}},watch: {js: {files: ['src/js/*.js'],tasks: ['babel']},css: {files: ['src/scss/*.scss'],tasks: ['sass']}}})// grunt.loadNpmTasks('grunt-sass')loadGruntTasks(grunt) // 自动加载所有的grunt插件中的任务grunt.registerTask('default', ['sass', 'babel', 'watch'])
}

3、Gulp

Gulp是目前世界上最流行的前端构建系统,其核心特点就是高效、易用。它很好的解决了Grunt中读写磁盘慢的问题,Gulp是基于内存操作的。Gulp支持同时执行多个任务,效率自然大大提高,而且它的使用方式相对于Grunt更加易懂,而且Gulp的生态也非常完善,所以后来居上,更受欢迎。

  • gulp的使用

gulp-sample>npm install --global gulp-cli  //安装 gulp 命令行工具

npm install --save-dev gulp   //作为开发时依赖项

// gulp的入口文件
exports.foo = done => {console.log('foo task working...')done() // 使用done()标识任务完成
}exports.default = done => {console.log('default task working...')done()
}

执行命令:gulp foo执行foo任务, 或者yarn gulp执行默认任务default

gulp4.0之前的任务写法:

const gulp = require('gulp')gulp.task('bar', done => {console.log('bar working...')done()
})

执行命令gulp bar可以运行bar任务,gulp4.0之后也保留了这个API,但是不推荐使用了

  • gulp创建组合任务:series串行、parallel并行
const {series, parallel} = require('gulp')// gulp的入口文件
exports.foo = done => {console.log('foo task working...')done() // 标识任务完成
}exports.default = done => {console.log('default task working...')done()
}const task1 = done => {setTimeout(() => {console.log('task1 working...')done()}, 1000);
}const task2 = done => {setTimeout(() => {console.log('task2 working...')done()}, 1000);
}const task3 = done => {setTimeout(() => {console.log('task3 working...')done()}, 1000);
}// series 串行执行
// exports.bar = series(task1, task2, task3)// parallel 并行执行
exports.bar = parallel(task1, task2, task3)
  • Gulp的异步任务:
const fs = require('fs')exports.callback = done => {console.log('callback task...')done() // 通过使用done()标志异步任务执行结束
}exports.callback_error = done => {console.log('callback task...')done(new Error('task failed!')) // done函数也是错误优先回调函数。如果这个任务失败了,后序任务也不会工作了
}exports.promise = () => {console.log('promise task...')return Promise.resolve() // resolve执行的时候,表示异步任务执行结束了。resolve不需要参数,因为gulp会忽略它的参数
}exports.promise_error = () => {console.log('promise task...')return Promise.reject(new Error('task failed')) // reject标志这是一个失败的任务,后序的任务也会不再执行
}const timeout = time => {return new Promise(resolve => {setTimeout(resolve, time);})
}
exports.async = async() => {await timeout(1000) // 在node8以上可以使用async和await,await的就是一个Promise对象console.log('async task...')
}exports.stream = (done) => { // 最常用的就是基于stream的异步任务const readStream = fs.createReadStream('package.json')const writeSteam = fs.createWriteStream('temp.txt')readStream.pipe(writeSteam)return readStream  // 相当于下面的写法// readStream.on('end', () => {//    done()// })
}
  • Gulp构建过程,例子:压缩CSS
const fs = require('fs')
const {Transform} = require('stream')exports.default = () => {const read = fs.createReadStream('normalize.css')const write = fs.createWriteStream('normalize.min.css')// 文件转化流const transform = new Transform({transform: (chunk, encoding, callback) => {// 核心转化过程// chunk => 读取流中读取的内容(Buffer )const input = chunk.toString()// 转化空白符和注释const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')callback(null, output)}})read.pipe(transform) // 先转化.pipe(write)return read
}
  • Gulp文件api
const {src, dest} = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')exports.default = () => {return src('src/*.css').pipe(cleanCss()).pipe(rename({ extname: '.min.css' })).pipe(dest('dist'))
}
  • Gulp构建
// 实现这个项目的构建任务
const {src, dest, parallel, series, watch} = require('gulp')const del = require('del')
const browserSync = require('browser-sync')const bs = browserSync.create()const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()const {sass, babel, swig, imagemin} = pluginsconst data = {menus: [{name: 'Home',icon: 'aperture',link: 'index.html'},{name: 'Features',link: 'features.html'},{name: 'About',link: 'about.html'},{name: 'Contact',link: '#',children: [{name: 'Twitter',link: 'https://twitter.com/w_zce'},{name: 'About',link: 'https://weibo.com/zceme'},{name: 'divider'},{name: 'About',link: 'https://github.com/zce'}]}],pkg: require('./package.json'),date: new Date()
}const clean = () => {return del(['dist', 'temp'])
}const style = () => {return src('src/assets/styles/*.scss', { base: 'src' }).pipe(sass({ outputStyle: 'expanded' })).pipe(dest('temp')).pipe(bs.reload({stream: true}))
}const script = () => {return src('src/assets/scripts/*.js', { base: 'src' }).pipe(babel({ presets: ['@babel/preset-env'] })).pipe(dest('temp')).pipe(bs.reload({stream: true}))
}const page = () => {return src('src/**/*.html', {base: 'src'}).pipe(swig(data)).pipe(dest('temp')).pipe(bs.reload({stream: true}))
}const image = () => {return src('src/assets/images/**', {base: 'src'}).pipe(imagemin()).pipe(dest('dist'))
}const font = () => {return src('src/assets/fonts/**', {base: 'src'}).pipe(imagemin()).pipe(dest('dist'))
}const extra = () => {return src('public/**', {base: 'public'}).pipe(dest('dist'))
}const serve = () => {watch('src/assets/styles/*.scss', style)watch('src/assets/scripts/*.js', script)watch('src/*.html', page)watch(['src/assets/images/**','src/assets/fonts/**','public/**'], bs.reload)bs.init({notify: false,port: 2080,open: false,// files: 'temp/**',server: {baseDir: ['temp', 'src', 'public'], // 按顺序查找routes: {'/node_modules': 'node_modules'}}})
}const useref = () => {return src('temp/*.html', { base: 'temp' }).pipe(plugins.useref({ searchPath: ['temp', '.'] })).pipe(plugins.if(/\.js$/, plugins.uglify())).pipe(plugins.if(/\.css$/, plugins.cleanCss())).pipe(plugins.if(/\.html$/, plugins.htmlmin({collapseWhitespace: true,minifyCSS: true,minifyJS: true}))).pipe(dest('dist'))
}// const compile = parallel(style, script, page, image, font)
const compile = parallel(style, script, page)// 上线之前执行的任务
const build = series(clean,parallel(series(compile, useref),image,font,extra)
)// 开发阶段
const develop = series(compile, serve)module.exports = {clean,compile,build,develop,
}

其中依赖文件如下:

"devDependencies": {"@babel/core": "^7.10.2","@babel/preset-env": "^7.10.2","browser-sync": "^2.26.7","del": "^5.1.0","gulp": "^4.0.2","gulp-babel": "^8.0.0","gulp-clean-css": "^4.3.0","gulp-htmlmin": "^5.0.1","gulp-if": "^3.0.0","gulp-imagemin": "^7.1.0","gulp-load-plugins": "^2.0.3","gulp-sass": "^4.1.0","gulp-swig": "^0.9.1","gulp-uglify": "^3.0.2","gulp-useref": "^4.0.1"},

4、FIS

FIS是百度的前端团队推出的构建系统,FIS相对于前两种微内核的特点,它更像是一种捆绑套餐,它把我们的需求都尽可能的集成在内部了,例如资源加载、模块化开发、代码部署、甚至是性能优化。正式因为FIS的大而全,所以在国内流行。FIS适合初学者。

全局安装:npm install -global fis3

执行fis3 release

开发脚手架与自动化构建相关推荐

  1. 大前端学习--开发脚手架与自动化构建工作流封装

    开发脚手架与自动化构建工作流封装 去年6月24号开始工作,到今天刚好一周年了,纪念一下,分享最近学习的前端工程化笔记. 一.前端工程化 前端工程化是指遵循一定的标准和规范,通过工具去提高效率.降低成本 ...

  2. 前端工程化——脚手架及自动化构建

    定义 一切以提高效率.降低成本.质量保证为目的的手段,都属于工程化 前端工程化主要解决的问题 传统语言或语法的弊端 无法使用模块化/组件化 重复性的机械工作 代码风格统一.质量保证 依赖后端服务接口的 ...

  3. 「移动开发」iuap mobile玩转前端自动化构建

    提起iuapmobile studio,相信大家已经不再陌生了,是进行移动应用开发的强大集成工具与必备神器.借助强大的iuap mobile studio,可以实现对开发.测试.调试.打包.发布全程管 ...

  4. altium designer无法创建工程_前端工程化之开发脚手架及封装自动化构建

    工程化笔记 .  . 光 前端工程化简述 简述 一切能提升前端开发效率,提高前端应用质量的手段和工具都是前端工程化. 模块化,组件化,规范化,自动化. 解决的问题 传统语言或语法的弊端. 无法使用模块 ...

  5. 开发脚手架及封装自动化构建工作流

    前端工程化 概述 1.多人协作开发,无法硬性统一大家的代码风格 从仓库中pull回来的代码质量无法保证 2.部分功能开发时需要等待后端服务接口提前完成 3.传统语言或语法的弊端 4.无法使用模块化/组 ...

  6. 【Linux】三、Linux 环境基础及开发工具使用(上篇)|开发工具|编辑器-vim使用|sudo提升权限问题|编译器 - gcc/g++使用|项目自动化构建工构建工具-make/Makefile

    目录 一.开发工具 二.Linux编辑器 - vim使用 2.1 vim 的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2. ...

  7. 前端自动化构建工具:用Gulp4.0搭建一个基本的前端开发环境

    一个项目的完成一般需要经过三个步骤:初始化.开发和部署.在前端开发过程中,我们经常需要依靠一些自动化构建工具来优化前端工作流程,来帮助我们完成一系列繁琐的工作,例如浏览器热更新.ES6编译.代码压缩. ...

  8. ios架构与开发第二课 代码规范管理与自动化构建

    05 自动化准备:如何使用 Fatlane 管理自动化操作? 要成为一个优秀的 iOS 开发者,我们要做的事情远多于"开发",例如我们要构建和打包 App,管理证书,为 App 进 ...

  9. 近期总结:generator-web,前端自动化构建的解决方案

    本文结合最近的工作经验,总结出一个较简洁的前端自动化构建方案,主张css和js的模块化,并通过grunt的自动化构建,有效地解决css合并,js合并和图片优化等问题,对于提高前端性能和项目代码质量有一 ...

最新文章

  1. Java调用net的webservice问题分享
  2. 记事本 换行符_Windows 记事本再度上架 Microsoft Store
  3. 使用SAP云平台 + JNDI访问Internet Service
  4. CSS中的块元素,内联元素,内联块元素
  5. 钉钉老版本下载3.31_钉钉3.3.1老版本官方下载-钉钉3.3.1旧版本PC版官方版-东坡下载...
  6. python中的保护对象数据
  7. 2017-9-26 NOIP模拟赛
  8. windwos cakephp mysql apache_配置Apache2.2+PHP5+CakePHP1.2+MySQL5运行环境
  9. 剪切caspase3_caspase-3
  10. linux机器的物理内存监控,Linux内存监控工具
  11. 多媒体实时交互系统主要由系统服务器,多媒体设备和多媒体交互系统专利_专利申请于2017-03-08_专利查询 - 天眼查...
  12. 在Android浏览器下字体偏上的问题
  13. golang-ffmpeg-goav:视频拉流解码成YUVJ420P
  14. linux解压带密码zip,linux下解压有密码的rar压缩包的方法
  15. 域名解析CNAME和MX记录 冲突 解决办法
  16. 蓝牙LMP剖析(一)
  17. Android集成高德地图
  18. 齐岳|聚乙二醇-四氧化三铁-二氢卟吩纳米复合物Fe3O4-PEG-Ce6|肝靶向功能的四氧化三铁Fe3O4纳米粒子
  19. 正则表达式匹配仅包含某些字符或者包含某些字符
  20. 【论文阅读】Multi-Modal Sarcasm Detection 图文反讽识别

热门文章

  1. 高德地图面积计算公式
  2. eslint报“Extra semicolon“错误的解决
  3. C#快速调用海康威视工业相机的SDK拍照获取图片
  4. 端午特供——小朋友都会写的【狂扁·大粽子】
  5. 51单片机检测温湿度并且上传到阿里云
  6. IBM WebSphere 9.0.5 笔记大全
  7. 是什么引发了我对知识的渴望
  8. 了解cuda和显卡等基本概念
  9. OMAPL138的DSPLINK开发入门
  10. 黑苹果从入门到精通:可能是世界上最详细的VMware安装macOS教程