工程化是一个能够提高开发体验、提升开发效率和质量的规划或者工做流架构,一切以提升效率、下降成本、质量保证为目的的手段都属于工程化。javascript
工程化带来的价值:css
更深的意义在于,脚手架也为咱们提供了项目规范和公共约定。包括相同的组织结构、相同的开发范式、相同的模块依赖、相同的工具配置、相同的基础代码等等,对于公司里的大多数产品,前端均可以使用同一套脚手架,不只能够统一各个项目,当项目成员切换团队时,也能够直接上手,提升效率,相关模块依赖更新或者配置须要改动时,能够一步到位更新全部产品,更利于维护。前端
脚手架的实现流程vue
基于 NodeJS 的自定义小型脚手架工具 lxc-scaffolding,参见 https://github.com/luxiancan/...java
参见 https://github.com/luxiancan/...node
还没完成。。。react
https://github.com/luxiancan/...git
经过工程化提高战斗力github
建立项目 => 编码 => 预览/测试 => 提交 => 部署vue-cli
一些成熟的工程化集成
前端工程化的发起者
脚手架工具的本质做用:建立项目基础结构、提供项目规范和约定。
它们能够根据信息建立对应的项目基础结构
Yeoman: 一款通用型脚手架工具,建立项目时,能够根据模板生成对应的项目结构,很灵活、易于扩展
Plop: 用于在开发过程当中,建立一些特定类型的文件
$ npm install yo --global # or yarn global add yo
$ npm install generator-node --global # or yarn global add generator-node
$ cd path/project-dir $ mkdir my-module $ cd my-module $ yo node
Generator 基本结构
├── generators/ ·········································· 生成器目录 │ ├── app/ ············································· 默认生成器目录 │ | └── index.js ···································· 默认生成器实现 | └── component/ ······································· 其余生成器目录 │ └── index.js ···································· 其余生成器实现 └── package.json ········································· 模块包配置文件
自定义 Generator 的详细操做步骤:
1.建立 generator 文件目录
$ mkdir generator-lxc-test $ cd generator-lxc-test
2.给项目初始化一个 package.json$ npm init -yes # or yarn init -yes
3.安装 yeoman 依赖$ yarn add yeoman-generator
4.新建 generator 入口文件$ code generators/app/index.js
// 此文件做为 Generator 的核心入口 // 须要导出一个继承自 Yeoman Generator 的类型 // Yeoman Generator 在工做时会自动调用咱们在此类型中定义的一些生命周期方法 // 咱们在这些方法中能够经过调用父类提供的一些工具方法实现一些功能,例如文件写入 const Generator = require('yeoman-generator'); module.exports = class extends Generator { prompting () { 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; }); } // Yeoman 自动在生成文件阶段调用此方法 // 咱们这里尝试往项目中写入文件 writing () { this.fs.write( this.destinationPath('temp.txt'), Math.random().toString() ) } }
5.若是使用到模板,须要在 app 目录下新建 templates 目录存放模板文件,使用 EJS 语法写入变量
这是一个模板文件 内部可使用 EJS 模板标记输出数据 例如:<%= title %> 其余的 EJS 语法也支持 <% if (success) { %> 哈哈哈 <% }%>
6.完成 index.js 后,将咱们建立的 generator 连接到全局,在 generator-lxc-test 目录下执行:$ yarn link
7.使用咱们建立好的 generator
$ cd path/project-dir $ mkdir my-test-proj $ cd my-test-proj $ yo lxc-test
1.安装 plop $ yarn add plop --dev
2.在项目根目录下建立模板文件 plop-templates/component.hbs
<template> <div class="{{name}}"> <h1>{{name}} Component</h1> </div> </template> <script> export default { name: '{{name}}', data() { return {}; }, } </script> <style scoped lang="scss"> .{{name}} {} </style>
3.在项目根目录下建立 plop 入口文件 plopfile.js
// 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', // add 表明添加文件 path: 'src/components/{{name}}/{{name}}.vue', templateFile: 'plop-templates/component.hbs' } ] }) }
4.利用咱们刚刚注册的生成器的名字启动 plop$ yarn plop component
一切重复工做本应自动化
源代码 => 自动化构建 => 生产代码
自动化构建工做流
使用提升效率的语法、规范和标准
利用 package.json 中的 script 属性,配置一些简单的自动构建命令
"scripts": { "build": "sass scss/main.scss css/style.css --watch", "serve": "browser-sync . --files \"css/*.css\"", "start": "run-p build serve" },
// gruntfile.js module.exports = grunt => { grunt.registerTask('foo', () => { console.log('hello grunt~'); }); grunt.registerTask('bar', '任务描述', () => { console.log('other task~'); }); grunt.registerTask('default', ['foo', 'bar']); // 执行异步任务 grunt.registerTask('async-task', function () { const done = this.async(); setTimeout(() => { console.log('async task working~'); done(); }, 3000); }); }
const done = this.async(); ... done(false);
module.exports = grunt => { grunt.initConfig({ // foo: 'bar' foo: { bar: 123 } }); grunt.registerTask('foo', () => { // const foo = grunt.config('foo'); // console.log(foo); // bar const bar = grunt.config('foo.bar'); console.log(bar); // 123 }); }
module.exports = grunt => { grunt.initConfig({ clean: { // temp: 'temp/app.js' // temp: 'temp/*.txt' temp: 'temp/**' } }); grunt.loadNpmTasks('grunt-contrib-clean'); }
yarn init -yes
yarn add grunt grunt-sass sass --dev
建立 gruntfile.js 文件
yarn grunt sass
yarn add grunt-babel @babel/core @babel/preset-env --dev
yarn add load-grunt-tasks --dev
yarn init -yes
yarn add gulp
建立 gulpfile.js 文件
yarn gulp my-task 执行指定任务
yarn gulp 执行默认的 default 任务
// gulpfile.js gulp 的入口文件 exports.foo = done => { console.log('foo task working~'); done(); // 标识任务完成 } exports.default = done => { console.log('default task working~'); done(); } // gulp 4.0 版本以前,建立任务的方式 const gulp = require('gulp'); gulp.task('bar', done => { console.log('bar working~'); done(); });
const { series, parallel } = require('gulp'); const task1 = done => { setTimeout(() => { console.log('task1 working~'); done(); }, 1000); } // ... // 建立串行任务 exports.foo = series(task1, task2, task3); // 建立并行任务 exports.bar = parallel(task1, task2, task3);
exports.promise = () => { console.log('promise task~'); return Promise.resolve(); } exports.promise_error = () => { console.log('promise task~'); return Promise.reject(new Error('task failed!')); } const timeout = time => { return new Promise(resolve => { setTimeout(resolve, time); }); }; exports.async = async () => { await timeout(1000); console.log('async task~'); } // 处理文件流 const fs = require('fs'); exports.stream = done => { const readStream = fs.createReadStream('package.json'); const writeStream = fs.createWriteStream('temp.txt'); readStream.pipe(writeStream); readStream.on('end', () => { done(); }); }
输入 => 加工 => 输出读取流 => 转换流 => 写入流