大前端【2-1笔记】开发脚手架及封装自动化构建工作流
前端工程化
一、前端工程化
1、定义
前端工程化是指遵循一定的标准和规范通过工具提高效率的一种手段。技术是为了解决问题而存在的,主要解决的问题
- 传统语言和语法的弊端
- 无法使用模块化/组件化
- 重复的机械性工作
- 依赖后端接口支持
- 整体依赖后端项目
- 很难做到代码风格统一、质量难以做保证
2、工程化表现
一切以提高效率、降低成本、质量保证为目的的手段都属于工程化
二、脚手架工具
脚手架可以理解为,自动为项目创建基础结构、给开发者提供项目规范和约定的工具。例如:使用IDE创建一个项目,过程其实就是一个脚手架的工作流程。常用的脚手架工具:
- React.js---->create-react-app
- Vue.js---->vue-cli
- Angular----->angular-cli
三、Yeoman
1、介绍
Yeoman 是一种高效、开源的 Web 应用脚手架搭建系统,意在精简开发过程
- Yeoman 提供了一种灵活创建、开发、编译和调试 Web 应用的脚手架(scaffolding)软件
- Yeoman 本身是 JavaScript 编写的,但适用于任何语言编写的应用
- Yeoman 支持与 Webpack、Babel、TypeScript、React 和 Angular 等多种第三方软件库的无缝集成
2、安装使用
yarn安装
npm install -g yarn
在全局范围内安装
yarn global add yo // npm install yo --global
安装对应的generator
yarn global add generator-node // npm install generator-node --global
通过yo运行generator
yo node
3、自定义Generator
generator本质就是一个NPM模块,generator有基本结构
generators //生成器目录
|---app //默认生成器任务
|---index.js //默认生成器实现
package.json //模块包配置文件
路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VVX68wMD-1594469395305)(C:\Users\张艳杰\AppData\Roaming\Typora\typora-user-images\1594049065813.png)]
yarn init //创建package.json
yarn add yeoman-generator //提供生成器的基类
index.js
// 此文件作为Generator的核心入口
// 需要导出一个继承自Yeoman Generator 的类型
// Yeoman 在工作时会自动调用我们在此类型中定义的一些生命周期方法
// 我们在这些方法中可以通过调用父类提供的工具方法实现一些功能,例如文件写入
const Generator = require("yeoman-generator"); //载入生成器
module.exports = class extends Generator {
writing() {
this.fs.write(
this.destinationPath('demo.text'),
Math.random().toString()
);
}
}
yarn link //使用该命令,将模块链接到全局范围
yo sample //yo + name 生成器的名字,
问问题prompting()
prompting(){
return this.prompt([
{
type:'input',
name:'name',
message:'your project name',
default:this.appname
}
]).then(answers => {
this.answers = answers
})
}
使用模板创建文件 writing( )
writing() {
//模板文件路徑
const tmpl = this.templatePath('bar.html')
//输出路径
const output = this.destinationPath('bar.html')
//模板数据上下文
const context = this.answers
this.fs.copyTpl(tmpl,output,context)
}
四、自动化构建
开发行业中的自动化构建就是把我们在开发环境的源代码自动化的转化为生产环境中可运行的代码
1、Gulp 完成项目的自动化构建
(1)准备工作
mkdir my-gulp
cd my-gulp
yarn init --yes //初始化package文件
(2)安装gulp
yarn add gulp --dev //安裝gulp模块
(3)创建gulpfile.js文件
// gulpfile.js
//gulp的入口文件
exports.foo = (done)=>{
console.log("foo is working")
//使用 yarn gulp foo执行,发现报错,因为gulp取消了同步任务,规定为异步任务,可以接受一个参数,最后调用参数标识任务的完成
done() //标识任务完成
}
使用默认,yarn gulp 即可定位运行到default
exports.default = done => {
console.log('default')
done()
}
gulp4.0以前的定义方式,依旧保留此api但是不被推荐使用
const gulp = require('gulp')
gulp.task('bar',done=>{
console.log("bar...")
done()
})
(4)组合任务
通过相关api创建并行任务和串行任务
未被导出的函数可以理解为私有任务,例如以下为三个私有任务
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,parallel进行并行和串行的操作
const { series, parallel, task } = require("gulp")
exports.foo = series(task1, task2, task3) //串行
exports.bar = parallel(task1,task2,task3) //并行
//测试
yarn gulp foo
yarn gulp bar
(5)异步任务
gulp都是异步任务,我们可以通过传参来抛出异常
exports.callback = done => {
console.log('callback~~')
done()
}
exports.callback_error = done => {
console.log('callback_error~~')
//可以通过传参的方式来抛出一个错误,错误优先
done(new Error('task_error'))
}
通过promise來控制任务的成功与否
exports.promise_success = done => {
console.log('promise')
//不需要为resolve传递任何值,以为gulp会忽略
return Promise.resolve()
}
exports.promise_fail = done => {
console.log('promise')
//传递具体的错误信息
return Promise.reject(new Error('promise fail'))
}
使用async await 进行
const timeout = time => {
return new Promise(resolve => {
setTimeout (resolve,time)
})
}
exports.async = async () => {
await timeout(1000)
console.log('async task~~~')
}
基于Stream的异步
exports.stream = () => {
const readStream = fs.createReadStream('package.json')
const writeStream = fs.createWriteStream("tmp.txt")
readStream.pipe(writeStream)
return readStream
}
(6)构建过程
对css进行压缩,代码如下
const fs = require("fs")
const { Transform } = require("stream")
exports.default = () => {
//文件读取流
const read = fs.createReadStream("nomarlize.css")
//文件写入流
const write = fs.createWriteStream("nomarlize.min.css")
//文件转换流
const transform = new Transform({
transform:(chunk,encoding,callback)=>{
//核心转化过程实现
//chunk => 读取流中读取到的内容(buffer)
const input = chunk.toString()
const output = input.replace(/\s+/g,'')
callback(null,output)//错误有限,如果没有错误,则传入null
}
})
//先转化后写入
read.pipe(transform).pipe(write)
return read
}
(7)文件操作API
src,dest
const {src,dest} = require('gulp')
//将src下的文件映射到dist
exports.default = () => {
return src('src/normalize.css').pipe(dest('dist'))
}
使用通配符,將src下边的以css结尾的文件都会被复制
exports.default = () => {
return src('src/*.css').pipe(dest('dist'))
}
安装模块,转化css代码
yarn add gulp-clean-css --dev
yarn add gulp-rename --dev
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'))
}
(8)演示
1、找骨架,克隆下来
git clone https://github.com/zce/zce-gulp-demo.git
2、准备工作
yarn add gulp --dev
yarn add gulp-sass --dev
3、样式编译
//gulp的入口文件
//导入文件路径API
const { dest, src } = require('gulp')
const sass = require('gulp-sass')
const style = () => {
/**
* 但是并没有与src一样的路径。所以需要指定 {base:'src'}
*/
return src('src/assets/styles/*.scss', { base: 'src' }).pipe(sass())
.pipe(dest('dist'))
}
module.exports = {
style
}
4、script文件,脚本编译
添加依赖
yarn add gulp-babel --dev
yarn add @babel/core @babel/preset-env --dev //安装核心模块和env模块,env模块默认会吧ECMAScript新特性都会进行转换
需要在babel中传递presets:[’@babel/preset-env’],因为babel只是一个平台,不做任何事情,所以会根据babel内部的设置进行修改
//引入
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
}
测试
yarn gulp script
5、页面模板编译
安装插件
yarn add gulp-swig --dev
引用依赖,任务编写
const swig = require('gulp-swig')
//注意:data是数据,可以将其替换到页面中
const page = () => {
return src("src/*.html",{base:'src'}).pipe(swig({data})).pipe(dest('dist'))
}
module.exports = {
page
}
6、compile编译
引用gulp中的parallel
const { dest, src ,parallel} = require('gulp')
组合任务
const compile = parallel(style,page,script)
module.exports = {
compile
}
7、图片字体文件转化
安装依赖
yarn add gulp-imagemin --dev
创建任务
const imagemin = require('gulp-imagemin')
const image = () => {
return src("src/assets/images/**",{base:'src'}).pipe(imagemin()).pipe(dest('dist'))
}
const fonts = () => {
return src("src/assets/fonts/**",{base:'src'}).pipe(imagemin()).pipe(dest('dist'))
}
module.exports = {
image,fonts
}
8、其他文件及文件清除
创建任务
const extra = () => {
return src("public/**",{base:'public'}).pipe(dest('dist'))
}
module.exports = {
extra
}
9、自动加载插件
yarn add gulp-load-plugins --dev
不用每次都去引入插件,可以使用plugins自动引入插件
const loadPlugins = require('gulp-load-plugins')
const plugin = new loadPlugins()
const page = () => {
return src("src/*.html",{base:'src'}).pipe(plugin.swig({data})).pipe(dest('dist'))
}
10、开发服务器
安装依赖
yarn add browser-sync --dev
yarn add bootstrap@4.0.0-alpha.6 --dev
任务创建
const browserSync = require('browser-sync')
const bs = browserSync.create()
const serve = () => {
bs.init({
server: {
baseDir: 'dist',
routes: {
//处理静态文件,进行路由映射
'/node_modules': "node_modules"
}
}
})
}
module.exports = {
compile, image, fonts, extra, serve
}
测试:
yarn gulp serve
结果,成功运行:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ze2Q1FnS-1594469395310)(C:\Users\张艳杰\AppData\Roaming\Typora\typora-user-images\1594437518199.png)]
11、监视变化以及构建优化
解构watch
const { dest, src,series,parallel,watch } = require('gulp')
进行监听编写,监听对应的路径,执行对应的事件
const serve = () => {
watch("src/assets/styles/*.scss",style)
watch("src/assets/scripts/*.js",script)
watch("src/*.html",page)
watch("src/assets/images/**",image)
watch("src/assets/fonts/**",fonts)
watch("public/**",extra)
bs.init({
server: {
baseDir: 'dist',
files:'dist/**', //监听的是哪个文件
// open:false,
routes: {
'/node_modules': "node_modules"
}
}
})
}
测试,修改index,html,<span>Zce Gulp Demo</span>
改为<span>111Zce Gulp Demo</span>
页面呈现效果,已经热更改变
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kuzenNOo-1594469395312)(C:\Users\张艳杰\AppData\Roaming\Typora\typora-user-images\1594439616129.png)]
注意:
font与image以及额外的文件,热更没有太大的必要,因为图片压缩是无损压缩,太多的监视会一定程度上消耗性能,所以一般不对其就行监视,修改
watch("src/assets/styles/*.scss",style)
watch("src/assets/scripts/*.js",script)
watch("src/*.html",page)
//修改basedir
baseDir:["dist","public","src"]
2、使用grunt完成项目的自动化构建
(1)准备工作
mkdir my-grunt
yarn init --yes //创建package模块
yarn add grunt //安装grunt模块
(2)创建gruntfile.js文件
//Grunt的入口文件
//用于定义一些需要Grunt自动执行的任务
//需要导出一个函数,接收一个grunt的参数
module.exports = grunt => {
grunt.registerTask('foo',() => {
console.log("hello grunt")
})
}
//可以在第二个参数添加描述信息,会作为当前任务的描述信息存在
grunt.registerTask('bar',"任务描述",() => {
console.log("hello grunt")
})
//如果任务名称为default,则会作为默认任务存在,yarn grunt即可执行
grunt.registerTask('default',"任务描述",() => {
console.log("hello default grunt")
})
//也可通过数组进行任务组合,会依次执行数组中的任务,串联操作
grunt.registerTask("default",["foo","bar"])
注意:grunt默认执行同步任务,所以如果是异步任务,则需要通过this.async进行
grunt.registerTask("async-task",function(){
const done = this.async()
setTimeout(()=>{
console.log("async...")
done()
},1000)
})
(3)测试运行
yarn grunt foo
yarn grunt bar
yarn grunt default
yarn grunt async-task
(4)grunt标记任务失败
可以通过在任务体中直接return false实现
//grunt标记任务失败
grunt.registerTask('bad', "任务描述", () => {
console.log("bad")
return false
})
//异步标记失败
grunt.registerTask("bad-async",function(){
const done = this.async()
setTimeout(()=>{
console.log("bad-async")
done(false)
},1000)
})
如果是串行的任务,如果有一个出现问题,则后续任务不会被执行,但是可以指定参数--force
强制执行
grunt.registerTask("testBad",["foo","bad","bar"])
//执行
yarn grunt testBad --force
(5)grunt配置方法
module.exports = grunt => {
grunt.initConfig({
// foo:'bar',
foo:{
bar:123
}
})
grunt.registerTask('foo', () => {
// grunt.config 可以取出配置中foo对应的值
console.log(grunt.config('foo')) //属性值
console.log(grunt.config('foo.bar')) //对象值
})
}
(6)grunt多目标任务
多目标任务需要使用registerMultiTask进行任务的注册,必须要为对应的多目标任务在initConfig中添加target,如下
//grunt多目标任务
module.exports = grunt => {
grunt.initConfig({
build: {
//options作为任务的配置选项
options:{
foo:"bar"
},
css: {
options:{
foo:"baz" //会覆盖任务中的options
}
},
js: "2"
}
})
grunt.registerMultiTask('build', function () {
console.log(this.options())
console.log(`target:${this.target},data:${this.data}`)
})
}
(7)Grunt插件的使用
流程:安装插件载入插件根据插件文档完成相关选项
yarn add grunt-contrib-clean //安裝插件 自动清除临时文件
使用方法,使用loadNpmTasks载入插件
/**
* yarn add grunt-contrib-clean
* 自动清除临时文件
*/
module.exports = grunt => {
grunt.initConfig({
clean: {
// temp: 'temp/app.js'
// temp: 'temp/*.txt'
temp: 'temp/**'
}
})
grunt.loadNpmTasks('grunt-contrib-clean')
}
(7)grunt常用插件
- babel:将es6语法新特性进行转化
- sass:将sass转化为可识别的css
- html:将文件模板输出到指定目录
- watch:监视变化,热更新
- clean:清除指定文件夹的文件
sass
//安装依赖
yarn add grunt-sass sass -dev
//引入插件
const sass = require("sass")
//使用
module.exports = grunt => {
grunt.initConfig({
//sass转化为css功能
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
}
})
}
grunt.loadNpmTasks('grunt-sass')
js
//安裝插件
yarn add grunt-babel @babel/core @babel/preset-env --dev
//配置项中
module.exports = grunt => {
grunt.initConfig({
babel: {
options: {
sourceMap: true,
presets: ["@babel/preset-env"]
},
main: {
files: {
'dist/js/app.css': 'src/js/app.js'
}
}
},
})
}
html
//安裝
yarn add grunt-web-swig --dev
//使用
module.exports = grunt => {
grunt.initConfig({
web_swig: {
options: {
swigOptions: {
cache: false
},
getData: function (tpl) {
return { myGruntTitle: 'hello,grunt-web-swig' };
}
},
main: {
expand: true,
cwd: 'src/',
src: "**/*.html",
dest: "dist/"
},
},
})
}
grunt.loadNpmTasks('grunt-web-swig')
watch
//引入
yarn add grunt-contrib-watch --dev
//使用
module.exports = grunt => {
grunt.initConfig({
//监视功能
watch: {
js: {
files: ['src/js/*.js'],
tasks: ['babel']
},
css: {
files: ['src/scss/*.scss'],
tasks: ['sass']
},
html: {
files: ['src/*.html'],
tasks: ['web_swig', 'bs-reload']
}
},
})
}
grunt.loadNpmTasks('grunt-watch')
clean
//引入
yarn add grunt-contrib-clean --dev
//使用
module.exports = grunt => {
grunt.initConfig({
clean: {
files: 'dist/**'
}
})
}
grunt.loadNpmTasks('grunt-contrib-clean')
(8)loadGruntTasks
可以看到,我们每次引入一个模块,都要使用loadNpmTasks(name)
进行任务的注册,我们可以使用npm安装需要的插件,然后在gruntfile中使用loadGruntTasks载入所有安装的插件,最后完成相关的配置选项。
//安裝
yarn add load-grunt-tasks --dev
//使用
const loadGruntTasks = require("load-grunt-tasks")
//自动加载所有的grunt插件
loadGruntTasks(grunt)
(9)package.json
将命令通过scripts进行暴露,例如:可以直接“yarn clean”进行文件的清除
{
"name": "my-grunt",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"clean":"grunt clean",
"compile":"grunt compile",
"reload":"grunt bs-reload"
},
"dependencies": {
"@babel/core": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"grunt": "^1.2.1",
"grunt-babel": "^8.0.0",
"grunt-sass": "^3.1.0"
},
"devDependencies": {
"browser-sync": "^2.26.7",
"grunt-contrib-clean": "^2.0.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-tasks": "^1.0.0",
"grunt-web-swig": "^0.3.1",
"load-grunt-tasks": "^5.1.0",
"sass": "^1.26.10"
}
}
本文地址:https://blog.csdn.net/qiuqiu1628480502/article/details/107289982