工程化的价值
工程化是一样开发规范,一切以提高效率、降低成本、质量保证为目的手段都可称为'工程化'
在我日常开发过程中常常会遇到遇到问题
如:
- 语法新特性的兼容性问题
- 模块化兼容性问题
- 多人协作开发代码风格不统一
- 过于依赖后端接口数据
- 自动化部署的实现等问题
- 这些问题就需要工程化去解决
利用工程化主要能解决的问题有:
- 传统语言或语法的弊端
- 无法使用组件化/模块化
- 重复的机械式工作
- 代码风格统一、质量保证
- 依赖后端服务接口支持
- 整体依赖后端项目
- 自动化部署的实现
脚手架的意义
提供项目的规范和约定,如:
1.相同的组织结构
2.相同的开发范式
3.相同的模块依赖
4.相同的工具配置
4.相同的基础代码
自定义yeoman脚手架
1.安装脚手架工具
yarn add yo -g
验证是否安装成功:yo --version
2.安装yo成功后, 生成 generator
yarn add generator-generator -g
3.创建文件夹 mkdir generator-my-vue
4.在文件夹下运行yarn init --yes,初始化package.json
5.安装yeoman-generator
yarn add yeoman-generator --dev
6.创建入口文件genrator/app/index.js
const Generator = require('yoman-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() {
// 把每一个文件都通过模板转换到目标路径
const templates = [
'.browserslistrc',
'.editorconfig',
'.env.development',
'.env.production',
'.eslintrc.js',
'.gitignore',
'babel.config.js',
'package.json',
'postcss.config.js',
'README.md',
'public/favicon.ico',
'public/index.html',
'src/App.vue',
'src/main.js',
'src/router.js',
'src/assets/logo.png',
'src/components/HelloWorld.vue',
'src/store/actions.js',
'src/store/getters.js',
'src/store/index.js',
'src/store/mutations.js',
'src/store/state.js',
'src/utils/request.js',
'src/views/About.vue',
'src/views/Home.vue',
]
//循环模板,创建目录
templates.forEach((item) => {
// item => 每个文件路径
this.fs.copyTpl(this.templatePath(item), this.destinationPath(item), this.answers)
})
}
}
7.generator下创建 templates 里面放入要生成的模板
注:模板文件与index.js中的 templates 对应
- 运行 yarn link
9.创建新的文件夹,在文件下运行 yo my-vue
10.文件自动生成
nodejs自定义脚手架
-
创建文件夹 my-sample
-
进入 my-sample 文件夹 初始化 package.json
yarn init --yes
- 创建 全局链接
yarn link
- 在 package.json 中加入 bin 字段,用于指定入口文件
{
"name": "my-sample",
"version": "1.0.0",
"description": "nodejs脚手架",
"bin": "cli.js",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT"
}
- 安装 inquirer 和 ejs
yarn add inquirer
yarn add ejs
-根目录下创建 templates,在里面放入 index.html 和 styles.css
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= name %></title>
</head>
<body>
</body>
</html>
style.css
body {
margin: 0;
background-color: #f8f9fb;
}
- 根目录下创建 cli.js 文件,加入#!/usr/bin/env
#!/usr/bin/env node
//node Node CLI 应用入口文件必须要有这样的文件头
加入执行逻辑代码
#!/usr/bin/env node
// Node CLI 应用入口文件必须要有这样的文件头
// 如果是 Linux 或者 macOS 系统下还需要修改此文件的读写权限为 755
// 具体就是通过 chmod 755 cli.js 实现修改
// 脚手架的工作过程:
// 1. 通过命令行交互询问用户问题
// 2. 根据用户回答的结果生成文件
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const ejs = require('ejs')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Project name?'
}
])
.then(anwsers => {
// console.log(anwsers)
// 根据用户回答的结果生成文件
// 模板目录
const tmplDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 将模板下的文件全部转换到目标目录
fs.readdir(tmplDir, (err, files) => {
if (err) throw err
files.forEach(file => {
// 通过模板引擎渲染文件
ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => {
if (err) throw err
// 将结果写入目标文件路径
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})
使用说明:
项目中的模板有两个文件,只是作为例子,可根据自己的需求添加
注: index.html 中的 <title><%= name %></title> 表示 title 为用户定义
- 1.以上方模板为例,在项目中创建一个文件夹
- 2.在终端中打开该文件夹
- 3.运行 my-sample 4.提示? Project name? , 输入名称(该名称为 index.html 中的<title><%= name %></title>),点击回车
- 4.打开所创建的文件夹,里面出现了模板文件夹下的两个文件,内容 title 都一样, 表示创建成功
grunt构建工具使用
- 创建项目
- 在文件中运行 yarn init 初始化package.json
- 安装grunt依赖, yarn add grunt --dev
- 在根目录下创建gruntfile.js
gruntfile.js说明
- Grunt 的入口文件
- 用于定义一些需要Grunt 自动执行的任务
- 需要导出一个函数
- 此函数接收一个 grunt的形参,内部提供一些创建任务时可以用的API
//gruntfile.js
module.exports = (grunt) => {
grunt.registerTask('foo', () => {
console.log('hello, grunt')
})
}
运行:
yarn grunt foo
更多使用:
//gruntfile.js
module.exports = (grunt) => {
grunt.registerTask('foo', () => {
console.log('hello, grunt')
})
//第二个参数为描述
grunt.registerTask('bar', '任务描述', () => {
console.log('other task')
})
//默认任务, 直接运行 yarn grunt
grunt.registerTask('default', () => {
console.log('default task')
})
//两个任务一起执行
grunt.registerTask('default', ['foo', 'bar'])
//异步任务,不支持
// grunt.registerTask('async-task', () => {
// setTimeout(() => {
// console.log('async task working')
// }, 1000)
// })
//异步任务正确写法
grunt.registerTask('async-task', function () {
let done = this.async()
setTimeout(() => {
console.log('async task working')
done()
}, 1000)
})
//标记错误
grunt.registerTask('bad', () => {
console.log('error bar')
return false //return 后其他任务会执行
})
//异步错误
grunt.registerTask('async-error', function () {
let done = this.async()
setTimeout(() => {
console.log('async task error')
done(false)
}, 1000)
})
}
- initConfig 用于为任务添加一些配置选项
module.exports = grunt => {
// grunt.initConfig() 用于为任务添加一些配置选项
grunt.initConfig({
// 键一般对应任务的名称
// 值可以是任意类型的数据
foo: {
bar: 'baz'
}
})
grunt.registerTask('foo', () => {
// 任务中可以使用 grunt.config() 获取配置
console.log(grunt.config('foo')) //{bar:'baz'}
// 如果属性值是对象的话,config 中可以使用点的方式定位对象中属性的值
console.log(grunt.config('foo.bar')) //baz
})
}
- grunt 多目标模式
module.exports = grunt => {
// 多目标模式,可以让任务根据配置形成多个子任务
// grunt.initConfig({
// build: {
// foo: 100,
// bar: '456'
// }
// })
// grunt.registerMultiTask('build', function () {
// console.log(`task: build, target: ${this.target}, data: ${this.data}`)
// })
grunt.initConfig({
build: {
options: {
msg: 'task options'
},
foo: {
options: {
msg: 'foo target options'
}
},
bar: '456'
}
})
grunt.registerMultiTask('build', function () {
console.log(this.options())
})
}
- loadNpmTasks的使用
注 : 需要先下载 grunt-contrib-clean 和相关依赖
module.exports = grunt => {
grunt.initConfig({
clean: {
temp: 'temp/**'
}
})
grunt.loadNpmTasks('grunt-contrib-clean') //清除 temp 下面的所有文件
}
- load-grunt-tasks
注 : 需要先下载 load-grunt-tasks 和相关依赖
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt => {
grunt.initConfig({
sass: {
options: {
sourceMap: true,
implementation: sass
},
main: {
files: {
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
babel: {
options: {
sourceMap: true,
presets: ['@babel/preset-env']
},
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'])
}
Gulp
基本使用
// // 导出的函数都会作为 gulp 任务
// exports.foo = () => {
// console.log('foo task working~')
// }
// gulp 的任务函数都是异步的
// 可以通过调用回调函数标识任务完成
const { series, parallel } = require('gulp')
exports.foo = (done) => {
console.log('foo task working~')
done() // 标识任务执行完成
}
// default 是默认任务
// 在运行是可以省略任务名参数
exports.default = (done) => {
console.log('default task working~')
done()
}
// v4.0 之前需要通过 gulp.task() 方法注册任务
const gulp = require('gulp')
gulp.task('bar', (done) => {
console.log('bar 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)
}
// 让多个任务按照顺序依次执行
exports.foo1 = series(task1, task2, task3)
// 让多个任务同时执行
exports.bar = parallel(task1, task2, task3)
异步任务:
const fs = require('fs')
exports.callback = done => {
console.log('callback task')
done()
}
exports.callback_error = done => {
console.log('callback task')
done(new Error('task failed'))
}
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')
}
exports.stream = () => {
const read = fs.createReadStream('yarn.lock')
const write = fs.createWriteStream('a.txt')
read.pipe(write)
return read
}
// exports.stream = done => {
// const read = fs.createReadStream('yarn.lock')
// const write = fs.createWriteStream('a.txt')
// read.pipe(write)
// read.on('end', () => {
// done()
// })
// }
gulp 构建的核心工作原理
const fs = require('fs')
const { Transform } = require('stream')
exports.default = () => {
// 文件读取流
const readStream = fs.createReadStream('normalize.css')
// 文件写入流
const writeStream = fs.createWriteStream('normalize.min.css')
// 文件转换流
const transformStream = new Transform({
// 核心转换过程
transform: (chunk, encoding, callback) => {
const input = chunk.toString()
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')
callback(null, output)
}
})
return readStream
.pipe(transformStream) // 转换
.pipe(writeStream) // 写入
}
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'))
}
更多使用
const { src, dest, parallel, series, watch } = require('gulp')
const del = require('del')
const browserSync = require('browser-sync')
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const bs = browserSync.create()
const 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(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}
//脚本编译
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}
//转换html
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}
//转换图片
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin()) //压缩图片
.pipe(dest('dist'))
}
//转换字体
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.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/**', image)
// watch('src/assets/fonts/**', font)
// watch('public/**', extra)
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)
// 启动服务器运行文件
bs.init({
notify: false,
port: 2080,
// open: false,
// files: 'dist/**',
server: {
baseDir: ['temp', 'src', 'public'],
routes: {
'/node_modules': 'node_modules'
}
}
})
}
// useref 文件引用处理
const useref = () => {
return src('temp/*.html', { base: 'temp' })
.pipe(plugins.useref({ searchPath: ['temp', '.'] }))
// html js css
.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)
// 上线之前执行的任务
const build = series(
clean,
parallel(
series(compile, useref),
image,
font,
extra
)
)
const develop = series(compile, serve)
module.exports = {
clean,
build,
develop
}
将本地项目提交到git空仓库
- git init
- git remote add origin 仓库地址
- git status 查看状态
- git add .
- git commit -m '说明'
- git push -u origin master
vite 创建项目
npm init vite@latest
npm init vite-app projectName
git commit 验证eslint
npx mrm@2 lint-staged
网友评论