第一章: 概述
前端工程化: 提高效率, 降低成本, 保证质量
解决的问题:颜色代表不同问题分类
使用es6 兼容, lass sass 等无法直接运行, 模块化方式无法直接运行, 手动重复性动作(部署/压缩), 团队风格统一,github 拉的代码质量不可保证。 开发/整体依赖后端接口。
涉及领域:(对照下图)
第二章:脚手架(scaffolding) Yeoman
本质: 创建项目基础结构,规范和约定
Yeoman + generators => 各种脚手架
Yoeman 平A 技能
yeoman 使用 node-generator 创建 node modules 流程
Guohais-MBP:~ guohaiqu$ node -v |v16.0.0 |v10.16.2
Guohais-MBP:~ guohaiqu$ npm -v |7.19.0 |6.9.0
Guohais-MBP:~ guohaiqu$ yarn -v |1.22.10 |1.17.3
Guohais-MBP:~ guohaiqu$ yarn global add yo |全局安装 yoeman
Guohais-MBP:~ guohaiqu$ yarn global add generator-node |全局安装 generator-node
|进入文件|
Guohais-MBP:my_module guohaiqu$ yo node
|回答一些问题| => 安装
Yeoman Sub Generator (子生成器)
Guohais-MBP:my_module guohaiqu$ yo node:cli
Overwrite package.json yes
报错: premisson denied
sudo chmod -R 755 文件目录(my_module)
sudo chmod -R 755 文件目录(my_module/lib)
yarn unlink
yarn link
使用 yoeman + generator-webapp
yarn global add generator-wbapp
yo webapp
自定义Genertator = 创建npm 模块
1. 文件结构 固定(如下图,是否包含子生成器)
2. 文件名 固定 generator-filename
|| terminal
mkdir generator-sample // 创建生成器模块的目录
cd generator-sample // 进入文件夹
yarn init // 创建 package.json
yarn add yeoman-generator // 安装 模块 yeoman-generator = 提供生成器 基类
|| vs code
generators/app/index.js // 创建 index.js(generator核心入口)
||index.js
具体功能
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
writing() {
this.fs.write(
this.destinationPath('temp.txt'),
Math.random().toString()
)
}
}
Guohais-MacBook-Pro:generator-sample guohaiqu$ yarn link
cd ..
mkdir my-test
cd my-test
yo sample
添加模版文件
app/templates/foo.txt
// 模版文件创建
writing() {
const tmpl = this.templatePath('foo.txt')
const output = this.destinationPath('foo.txt')
const context = { title:'hello', success:false }
this.fs.copyTpl(tmpl, output, context)
}
// 命令行交互方法
prompting () {
return this.prompt([
{
type: "input",
name: "title",
message: "your project name", // 提示
default: this.appname // 项目目录名
},
{
type: "input",
name: "gg",
message: "second", // 提示
default: "gg"
}
])
.then (answers =>{
this.answers = answers
console.log(this.answers) //{ title: 'ggg', gg: 'yyy' }
})
}
// in foo.txt : eg: <%= title %><%= gg %>
批量添加模版文件
const Generator = require('yeoman-generator')
module.exports = class extends Generator {
prompting() {
return this.prompt([
{
type:"input",
name:"project_name",
message:"enter project name",
default: this.appname
}
])
.then(answers => {
this.answers = answers
})
}
writing() {
const templates = [
'public/first.html',
'public/first.css',
'src/second.html',
'src/second.css'
]
templates.forEach(item => {
this.fs.copyTpl(
this.templatePath(item),
this.destinationPath(item),
this.answers
)
});
}
}
.gitignore 忽略 node_modules
第三章:脚手架 Plop
是: 配合项目使用的小型脚手架
实例: react + plop 在react 中使用 plop 创建文件
1. 作为项目以来安装 plop | yarn add plop --dev |
2. 项目根下 :创建 plopfile.js , plop入口文件, 定义脚手架
3. 编写模版 hbs | plop-teamplates > filename.filetype.hbs ( handle bar)
4. plop cli 运行 脚手架 |yarn plop component/生成器名|
plopfile.js
plopfile.js :导出一个函数 ,| module.exports = () => {} |
其接收一个plop 对象(形式参数), 用来创建 generator | module.exports = plop => {} |
plop 对象 : > (成员) plop.setGenerator (参数1: 生成器名string , 参数2: 配置选项object)
配置选项 : description「string」 | prompts「array > object」 |actions 「array > object」
npx create-react-app my-app
yarn add plop --dev
module.exports = plop => {
plop.setGenerator ('component', {
description : "create component",
prompts: [
{
type: "input",
name: "name",
message: "component name",
default: "myComponent"
}
],
actions: [
{
type: "add",
path: "src/components/{{name}}/{{name}}.js",
templateFile: "plop-templates/component.js.hbs"
},
{
type: "add",
path: "src/components/{{name}}/{{name}}.css",
templateFile: "plop-templates/component.css.hbs"
},
{
type: "add",
path: "src/components/{{name}}/{{name}}.test.js",
templateFile: "plop-templates/component.test.js.hbs"
},
]
})
}
第四章:scaffolding 原理
创建项目 - 添加package.json 文件 - 添加"bin": "cli.js" 字段(指定cli 应用入口文件)
cli应用: 必备的两个条件 如下
#!/usr/bin/env node |必须添加文件头|
Guohais-MacBook-Pro:scaffolding-logic guohaiqu$ chmod 755 cli.js
「mac linux 修改 755 权限」
调用:
yarn link
scaffolding-logic(文件名)
step 1 : scaffolding logic - 询问信息
yarn add inquirer
const inquirer = require("inquirer")
inquirer.prompt([
{
type:"input",
name: "name",
message:"project name"
}
])
.then(anwsers =>{
console.log(anwsers)
})
// in console: scaffloding-logic (FILENAME)
step 2 : scaffolding logic - 添加文件(读取 + 写入)
const path = require("path") // 读取路径
const fs = require("fs") // 读取文件
const ejs = require("ejs") // 渲染文件
const tmpDir = path.join(__dirname, 'templates')
const distDir = process.cwd() // node process cwd 获取当前工作环境
fs.readdir(tmpDir,(err, files)=>{
if(err) throw err
files.forEach(file => {
ejs.renderFile(path.join(tmpDir, file), anwsers,(err, result)=>{
if(err) throw new err
fs.writeFileSync(path.join(distDir,file),result) //绝对路径,文件内容
})
})
})
第五章:自动化构建
一句话概述:自动转换代码| 如:scss - css
npm scripts 可自动发现 node_modules 下的命令, 在 bin 下面
yarn add sass --dev
//without npm scripts
./node_modules/.bin/sass scss/main.scss css/style.css
// with npm scripts
scripts:{
// NPM SCRIPTS : 自动找到node_modules\bin
build: "sass scss/main.scss css/style.css"
}
npm run build
测试服务器模块
yarn add browser-sync --dev
ATTENTION!
<head>
<style media="screen" type="text/css">
@import "css/style.css";
</style>
</head>
开启服务器前 需要先编译scss
方式1 : hook
"scripts": {
"build": "sass scss/main.scss css/style.css",
"preserve":"yarn build",
"serve":"browser-sync ."
},
方式2 : npm-run-all 模块
yarn add npm-run-all --dev
"scripts": {
"build": "sass scss/main.scss css/style.css --watch",
"serve": "browser-sync . --files \"css/*.css\"",
"start": "run-p build serve".
},
yarn start
解释:
scripts: { | 自动查找 bin 下的命令
--watch | 监听scss的文件变化, 改变就转译
--files \"css/*.css\" | 监听文件变化,改变就刷新页面
-- run-p | 同时运行多个命令
}
话题: 对比grunt gulp fis
grunt :速度慢|磁盘读写|生态完善|急于临时文件 | 微内核|灵活
gulp :速度相对块 |内存处理|默认多任务同时执行|易懂 |生态完善 |微内核|灵活
fis :捆绑套餐 |适合新手 |大而全 |适合初学者
第七章: Grunt
mkdir grunt | 创建文件目录
cd grunt | 进入项目
yarn init | 初始化package.json
yarn add grunt | 安装 grunt 模块
touch gruntfile.js | 创建 grunt 入口文件
gruntfile.js 入口文件 说明
1. gruntfile.js :导出一个函数 ,| module.exports = () => {} |
2. 其接收一个grunt 形式参数 | module.exports = grunt => {} |
3. grunt : > (成员) grunt.registerTask (参数1: 任务名 ,参数2(可选):任务描述stringm 参数3: 函数)
4. grunt.registerTask(‘default’,【“任务1”,“任务2”】)
备注: grunt 默认同步模式
备注:grunt 任务名 |执行特定任务
备注:grunt |执行默认任务
const { registerTask } = require("grunt") |导入grunt
module.exports = grunt => {
grunt.registerTask('foo','its a foo' , ()=>{ |同步任务
console.log('gg')
return false |同步任务标记失败
})
grunt.registerTask('bar', function(){ |异步任务
const done = this.async()
setTimeout(()=>{
console.log("2s delay")
done()
done(false) |异步任务标记失败
}, 1000)
})
grunt.registerTask('default', ['foo','bar']) |默认任务
}
// yarn grunt <function-name> | --force
多目标任务 和 配置选项
grunt.registerMutiTask ('参数1:任务名 ', 参数2: 函数 )
grunt.initConfig ({
任务名:{
options:{
foo: 'bar'
}
目标名1 : {
options: {
foo:'baz' // 覆盖
}
}
目标名2 : ‘目标2 执行的 任务’
}
})
${this.target} | 获取目标名
${this.data} |获取值的名
grunt.initConfig({
build: {
options: {
foo: 'baz' |为build 添加配置选项
},
js: {
options:{
foo:'bar' | 覆盖外圈的foo
},
val: 'something'
},
css: '123'
}
})
grunt.registerMultiTask('build', function(){
console.log(this.options()) |this.options是一个函数,调用他得到配置选项
console.log(this.data)
})
yarn grunt build
Running "build:js" (build) task
{ foo: 'bar' }
{ options: { foo: 'bar' }, val: 'gg' }
Running "build:css" (build) task
{ foo: 'baz' }
123
Grunt 插件
1. 安装插件 |2. 导入插件 |3. 配置选项
//1. 安装
yarn add grunt-contrib-clean
2. 导入
grunt.loadNpmTasks('grunt-contrib-clean')
3. 配置选项
grunt.initConfig({
clean: {
temp:'temp'
}
})
4. 使用
yarn grunt clean
yarn add grunt-sass sass --dev |grunt-sass 需要依赖sass 来完成
const sass = require('sass') |导入sass 模块
grunt.initConfig({
sass:{
options: {
sourceMap: true,
implementation: sass
},
main:{
files:{
'dist/css/main.css':'src/scss/main.scss'
}
}
}
})
grunt.loadNpmTasks('grunt-sass') |导入grunt-sass 任务
yarn add grunt-babel @babel/core @babel/preset-env --dev
grunt.initConfig {
main: {
options:{
sourceMap: true,
presets: ['@babel/preset-env'] |最新 ECMA 特性
},
files:{
'dist/js/app.js':'src/js/app.js'
}
}
}
grunt-contrib-watch --dev
grunt.initConfig({
watch:{
js:{
files: ['src/js/*js'], | 监视 js 文件
tasks: ['babel'] | 执行 babel 任务
}
}
})
grunt.registerTasks('default' ['babel', 'watch']) | 开始时先编译一下再监视
yarn add load-grunt-tasks --dev
const loadGruntTasks = require ('load-grunt-tasks')
loadGruntTasks(grunt)
第八章 - Gulp
预备: 安装 glup --dev| 2. gulpfile.js |gulp commands (yarn gulp <任务名>)
yarn init --yes
yarn add gulp --dev
touch gulpfile.js
导出任务
exports.foo = done =>{ // 传入异步任务
console.log('foo')
done() // 标记异步任务任务完成
}
exports.default = done =>{ // 默认任务
console.log('default')
done() // 标记异步任务任务完成
}
const gulp = require ('gulp') // 以前的方法
gulp.task('bar',done=>{
console.log('working')
done()
})
series + parallel 任务
const {series, parallel} = require('gulp') |导入顺序和并行执行
注: 未被导出的任务被视为私有
const task1 = done=>{
setTimeout(() => {
console.log('task1')
done()
},1000)
}
exports.foo = series(task1, task2, task3)
exports.bar = parallel(task1, task2, task3)
异步任务
//1. 回调函数实现异步任务
exports.callback = done =>{
console.log('callback function')
done()
}
exports.callback_err = done =>{
console.log('callback function')
done(new Error('task failed!')) //错误优先,第一个参数时错误 // 终止后续任务
}
2. promise 实现异步
exports.promise_error = () =>{
console.log('promise task!')
return Promise.reject(new Error('task failed!')) // 终止后续任务
// return Promise.resolve() // gulp 会忽略值, 因此留空就可
}
// 3. async await node环境限制 version>8
const timeout = time => {
return new Promise(resolve =>{
setTimeout(resolve, time)
})
}
exports.async = async()=>{
console.log(async)
await timeout(1000) // await 异步任务(promise 对象)
}
4. stream 最常见方式
exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream('temp.txt')
readStream.pipe(writeStream)
// done()
// return readStream
//readStream.on('end',()=>{
// done()
//})
}
核心原理
读取 转换 写入 :基于 流 的操作
const fs = require('fs')
const {Transfrom} = require ('stream')
exports.default =()=> { 导出默认任务
const read = fs.createReadStream(' file_url') 创建文件读取流
const write = fs.createWriteStream(' file_url') 创建文件读取流
const transform = new Transform({ 创建文件转换流
transform:(chunk, encoding, callback) {
const input = chunk.toString()
const output = input.reaplace('正则')
}
})
read
.pipe(transform)
.pipe(write)
}
Gulp 插件使用:
{ src dest } , gulp-clean-css, gulp-rename
安装 | 导入|放入pipe(something(明确参数))
const {src, dest} = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')
exports.default = () => {
return src('src/normalize.css')
.pipe(cleanCss())
.pipe(rename({extname:'.min.css'}))
.pipe(dest('dist'))
}
Gulp 案例:scss > css (gulp-sass)
{ base: "src" }, 导出文件的文件架构保留
sass({outputsytle: "expanded"}),使用sass 导出css 时的文件格式
const {src, dest} = require('gulp')
//const sass = require('gulp-sass') // gulp-sass 导入
const sass = require('gulp-sass')(require('sass')); // gulp-sass 5
const style = ()=>{
src('src/styles/scss/*.scss', {base: 'src'}) // 保留内部文件路径
.pipe(sass({outputsytle: "expanded"})) // css书写格式
.pipe(dest('dist'))
}
module.exports = { // = exports.style 另一种写法
style
}
Gulp 案例:ECMA (gulp-babel)
gulp-babel 是一个转换 平台
需要安装@babel/core @babel/preset-env 来转换(babely具体实施)
yarn add gulp-babel --dev
yarn add @babel/core @babel/preset-env --dev
const babel = require('gulp-babel');
const script = () => {
return src('src/assets/scripts/*.js', {base: "src"})
.pipe(babel({presets:['@babel/preset-env']}))
.pipe(dest('dist'))
}
module.exports = {
script
}
Gulp 案例:HTML (gulp-swig)
传入需要的 数据, 定义 data 对象, 传入 swig()
const {src, dest} = require('gulp')
const swig = require('gulp-swig')
const data = {
menus: [{},{},{}],
pkg: require('./package.json'),
data: new Data()
}
const page = () => {
src('src/**/*html')
.pipe(swig({defaults:{cache:false}})) // 加入清除缓存
//.pipe(swig({data})) //传入数据
.pipe(dest('dist'))
}
module.exports = {
page
}
Gulp 案例:ALL TOGETHER
const compile = parallel(style, script, page)
module.exports = {
compile
}
// yarn gulp compile
Gulp 案例:图片压缩 (gulp-imagemin)
yarn add gulp-imagemin --dev
const imagemin = require ('gulp-imagemin')
const image = () =>{
return src('src/**', {base: 'src'})
.pipe(imagemin())
.pipe(dest('dist'))
}
module.exports = {
image
}
// yarn gulp image
Gulp 案例:复制 + 删除 ( del :不是gulp插件)
其他文件如 public 下的 直接 复制源文件, src('....'). pipe(dest('dist'))
删除: yarn add del --dev
build 前删除 dist, 确保不覆盖
const del = require('del')
cosnt clean = () => {
return del(['dist'])
}
const build = series(clean, compile)
Gulp 案例: 自动加载插件 (grunt-load-plugins)
//只适用于 grunt 插件
const loadPlugins = require( 'grunt-load-plugins' )
const plugins = loadPlugins()
省略了: const imagemin = require( 'grunt-imagemin' )
使用插件时:pipe( plugins.imagemin() )
而不是: pipe(imagemin())
Gulp 案例: dev-server (browser-sync --dev: 不是gulp插件)
导入插件 | 创建服务器 |创建函数 | 定义配置
配置: notify |port |files|server(baseDir,r)|
const browserSync = require('browser-sync') // 引入 browser-sync
const bs = browserSync.create() // 创建服务器
const serve = ()=> {
bs.init({
notify: false, // 开启显示
port: 2080, // 设置服务器端口
files: "dist/**", // 监视dist 下文件的变化
server: {
baseDir: "dist", // 指定文件路径
routes: {
'/node_modules': 'node_modules'
}
}
})
}
监视dist 文件
添加init 配置, 监视文件
files: "dist/**", // 监视dist 下文件的变化 刷新页面
Gulp 案例: dev-server (监视src 文件变化)
const {watch} = require ('gulp') , 监视文件,判断知否执行任务
watch('参数1:路径',参数2: 任务)
开启server前使用
const serve = ()=> {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
Gulp 案例: useref
yarn add gulp-useref --dev
dist 中html 文件内的文件引用打包成vendor
.pipe( useRef( {searchPath:['dist','.']} ) )
const useRef = require('gulp-useref');
const ref = ()=>{
return src('dist/*.html',{base:'dist'})
.pipe(useRef({searchPath:['dist','.']}))
.pipe(dest('dist'))
}
Gulp 案例: 文件压缩(gulp-htmlmin,gulp-clean-css,gulp-uglify)
压缩html,css, js
yarn add gulp-if --dev
compile 完成后 会生成<start> <end> 字符, 根据此来进行压缩, 因此需要先compile 并在写入dist 前完成 压缩
const isif = require('gulp-if');
const htmlMin = require('gulp-htmlmin');
const cleanCss = require('gulp-clean-css');
const uglify = require('gulp-uglify');
const ref = ()=>{
return src('dist/*.html',{base:'dist'})
.pipe(useRef({searchPath:['dist','.']}))
.pipe(isif(/\.js$/, uglify()) )
.pipe(isif(/\.css$/, cleanCss()) )
.pipe(isif(/\.html$/, htmlmin({
collapseWhitespace:true,
minifyCSS:true,
minifyJS:true,
})) )
.pipe(dest('release'))
}
第九章: 可复用_自动化构建_工作流
使用脚手架创建新项目 => github 托管
复制 pulpfile.js 至 新项目入口文件 lib/index.js
将源中的devDependencies复制到目标的dependencies 并在目标项目中安装这些文件 yarn
删除 源: gulpfile.js 内容, devDependencies 字符,node_modules文件
将模版在全局link, yarn link
在源文件中 yarn link <name>pages
在源文件中有 scripts 字符, 运行他们,
关于.pipe(babel({ presets:[require('@babel/preset-env')] }))
require()会在自己目录下找,一直向上级找。
yarn add gulp-cli --dev
yarn add gulp --dev