前端工程化
概述
1.多人协作开发,无法硬性统一大家的代码风格 从仓库中pull回来的代码质量无法保证
2.部分功能开发时需要等待后端服务接口提前完成
3.传统语言或语法的弊端
4.无法使用模块化/组件化
5.重复的机械式工作
6.整体依赖后端项目
脚手架工具概要
1.创建项目基础结构、提供项目规范和约定
相同的组织结构
相同的开发范式
相同的模块依赖
相同的工具配置
相同的基础代码
2.常用脚手架工具
react项目:create-react-app
vue项目:vue-cli
angular项目:angular-cli
Yeoman
Yeoman
网站:https://yeoman.io/generators/
1.yarn global add yo(必须要搭配generator使用)
2.yarn global generator-node
3.yo node
4.eslint和babel实现 cli
yo node:cli
5.yarn link 全局命令使用
6.yarn 安装依赖
7.my-yeoman --help 使用
Yeoman使用步骤
1.明确你的需求
2.找到合适的generator
3.全局范围安装找到的generator
4.通过yo运行对应的generator
5.通过命令行交互填写选项
6.生成你所需要的项目结构
1.构建一个网页
yarn global add generator-webapp
2.运行这个generator
yo webapp
1.构建一个vue生成器目录
yarn init
yarn add yeoman-generator 提供生成器积累 工具函数 创建生成器更加便捷
2.创建一个generators/app/index.js 此文件作为generator的核心入口
需要导出一个继承自Yeoman Generator 的类型
Yeoman Generator在工作时会自动调用我们在此类型中定义的一些生命周期方法
我们在这些方法中可以通过调用父类提供的一些工具方法实现一些功能 列如文件写入
3.yarn link 链接到全局范围
grunt、gulp和FLS
1.grunt
优点:最早的前端构建工具 可以完成任何搭建工作
缺点:基于临时文件构建的 读写次数过多 构建速度慢
2.gulp
优点:基于内存实现的 处理文件在内存中 可同时执行多个任务 使用方式直观易懂 解决了grunt构建慢的问题
3.FLS(百度推行的)
绑定式的 自由度不高
Grunt 基本使用
(1)准备工作
mkdir my-grunt
cd my-grunt
yarn init --yes //初始化package文件
(2)安装gulp
yarn add grunt --dev //安裝gulp模块
(3)安装所需插件
//將sass转化为浏览器可识别的css文件
yarn add grunt-sass sass -dev
//将ECMAScript新特性转化
yarn add grunt-babel @babel/core @babel/preset-env --dev
//自动载入文件
yarn add load-grunt-tasks --dev
//监视文件变化
yarn add grunt-contrib-watch --dev
//清除指定文件夹下的文件
yarn add grunt-contrib-clean --dev
//浏览器
yarn add browser-sync --dev
//html文件编译处理
yarn add grunt-web-swig --dev
(3)创建gruntfile.js文件
//Grunt的入口文件
//用于定义一些需要Grunt自动执行的任务
//需要导出一个函数,接收一个grunt的参数
/**
* yarn add grunt-contrib-clean
* 自动清除临时文件
*/
const sass = require("sass")
const loadGruntTasks = require("load-grunt-tasks")
const browserSync = require("browser-sync")
const bs = browserSync.create()
module.exports = grunt => {
grunt.initConfig({
//sass转化为css功能
sass: {
options: {
sourceMap: true, //设置后会生成相应的sourceMap文件
implementation: sass
},
main: {
files: {
// 目标文件:源文件
'dist/css/main.css': 'src/scss/main.scss'
}
}
},
//js转化功能
babel: {
options: {
sourceMap: true,//设置后会生成相应的sourceMap文件
presets: ["@babel/preset-env"]
},
main: {
files: {
// 目标文件:源文件
'dist/js/app.css': 'src/js/app.js'
}
}
},
web_swig: {
options: {
swigOptions: {
//缓存设置为false
cache: false
},
getData: function (tpl) {
//模板文件的数据
return { myGruntTitle: 'hello,grunt-web-swig' };
}
},
main: {
expand: true,
cwd: 'src/',//源文件文件夹
src: "**/*.html",//后缀名匹配
dest: "dist/"//目标文件夹
},
},
//监视功能
watch: {
js: {
//文件地址
files: ['src/js/*.js'],
//执行的任务
tasks: ['babel']
},
css: {
//文件地址
files: ['src/scss/*.scss'],
//执行的任务
tasks: ['sass']
},
html: {
//文件地址
files: ['src/*.html'],
//执行的任务
tasks: ['web_swig', 'bs-reload']
}
},
//清除功能
clean: {
//所要清除的文件路径
files: 'dist/**'
}
})
// grunt.loadNpmTasks('grunt-sass')
loadGruntTasks(grunt) //自动加载所有的grunt插件
grunt.registerTask('default', ['clean', 'sass', 'babel', 'watch'])
grunt.registerTask('bs-reload', function () {
bs.reload()
})
grunt.registerTask('bs', function () {
const done = this.async()
bs.init({
server: {
port: 8080,
// baseDir: 'dist',
baseDir: ["dist", "public", "src"],
files: 'dist/**',
// open:false,
routes: {
'/node_modules': "node_modules"
}
}
})
})
grunt.registerTask('compile', ['clean', 'sass', 'babel'])
}
(4)package.json
{
"name": "my-grunt",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
// yarn clean ---->清除dist文件夹
"clean":"grunt clean",
// yarn compile ---->编译
"compile":"grunt compile",
//yarn reload ----->重载
"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"
}
}
1、概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具
(1)创建目录
(2)创建 package.json
yarn init
{
"name": "lrj-scaffolding", // 文件名 在全局使用时
"version": "0.1.0",
"main": "index.js",
"bin": "cli.js", // 指定cli文件的入口文件: “bin”:"cli.js"
"author": "lrj",
"license": "MIT",
"dependencies": {
"ejs": "^2.6.2", //询问
"inquirer": "^7.0.0" //添加模板引擎
}
}
(3)安装所需依赖
yarn/npm install
(5)创建cli.js文件
#!/usr/bin/env node
// 这段话的意思是让使用 node 进行脚本的解释程序,那下面的就可以使用 node 的语法了
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?',
default: 'my-project'
},
{
type: 'input',
name: 'desc',
message: 'Project description?'
}
])
.then(answers => {
// 根据用户回答的结果生成文件
// 拿到模板文件
const tmplDir = path.join(__dirname, 'templates');
// 目标目录
const destDir = process.cwd();
// 将模板下的文件全部转换到目标目录
fs.readdir(tmplDir, (err, files) => {
if (err) throw err;
files.forEach(file => {
// file => 文件相对于 templates 目录的相对路径
// 通过模板引擎渲染文件
ejs.renderFile(path.join(tmplDir, file), answers, (err, result) => {
if (err) throw err;
// 将结果写入目标文件
fs.writeFileSync(path.join(destDir, file), result);
});
});
})
});
(6)yarn link关联到全局变量
(7)使用
mkdir my-moudle //创建测试文件夹
cd my-moudle //切换文件夹
lrj-scaffolding //执行脚手架
2.尝试使用 Gulp 完成项目的自动化构建
(1)准备工作
mkdir my-grunt
cd my-grunt
yarn init --yes //初始化package文件
(2)安装gulp
yarn add grunt --dev //安裝gulp模块
(3)安装所需插件
"devDependencies": {
"@babel/core": "^7.5.5", //将ECMAScript新特性转化
"@babel/preset-env": "^7.5.5", //将ECMAScript新特性转化
"browser-sync": "^2.26.7", //启动一个服务
"del": "^5.1.0",// 清除文件插件
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0", //将ECMAScript新特性转化
"gulp-clean-css": "^4.2.0", //css压缩
"gulp-htmlmin": "^5.0.1", //html压缩
"gulp-if": "^3.0.0",// 判断
"gulp-imagemin": "^6.1.0", //图片压缩
"gulp-load-plugins": "^2.0.1", // 自动加载所有的 gulp 插件
"gulp-sass": "^4.0.2", //將sass转化为浏览器可识别的css文件
"gulp-swig": "^0.9.1", //html文件编译处理
"gulp-uglify": "^3.0.2", //js压缩
"gulp-useref": "^3.1.6", // 用于将所有文件打包到一个构建注释的文件中
"minimist": "^1.2.5", // 解析命令行参数
},
"dependencies": {
"bootstrap": "^4.3.1",
"jquery": "^3.4.1",
"popper.js": "^1.15.0"
}
(3)创建gruntfile.js文件
// 实现这个项目的构建任务
const { src, dest, parallel, series, watch } = require('gulp');
const path = require('path');
const minimist = require('minimist'); // 解析命令行参数
const del = require('del'); // 清除文件插件
// const Comb = require('csscomb'); // css编码风格格式化
// const standard = require('standard'); // js编码风格格式化
const borwserSync = require('browser-sync'); //建立一个服务
const loadPlugins = require('gulp-load-plugins'); // 自动加载所有的 gulp 插件
const plugins = loadPlugins(); //变成这个对象的属性
const bs = borwserSync.create();
const argv = minimist(process.argv.slice(2)); // 获取并处理命令行参数
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 }));
}
const page = () => {
// 'src/**/*.html' 匹配到 src 目录以及子目录下所有的html文件
// .pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
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 devServer = () => { //监测到更新 更新dist相应模块
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: argv.port === undefined ? 2080 : argv.port,
// open: false,
open: argv.open === undefined ? false : argv.open,
// 不使用 files 字段,使用 .pipe(bs.reload({ stream: true })); 也可以
// files: 'dist/**', // 监听文件的改变
server: {
baseDir: ['temp', 'src', 'public'],
routes: {
'/node_modules': 'node_modules'
}
}
})
};
const distServer = () => {
bs.init({
notify: false,
port: argv.port === undefined ? 2080 : argv.port,
open: argv.open === undefined ? false : argv.open,
server: 'dist'
});
}
// 使用 useref 之前需要执行 compile 编译
const useref = () => {
// 下载useref插件 <!-- build:css assets/styles/vendor.css --><!-- endbuild -->用于将所有文件打包到一个构建注释的文件中
// 这里文件名最好不要跟 dist 相同,防止文件读写冲突
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 serve = series(compile, devServer);
// 上线之前执行的任务。将 image, font 的任务放在这里
const build = series(clean, parallel(series(compile, useref), image, font, extra)) //组合任务
//启动打包后的任务
const start = series(build, distServer);
module.exports = {
clean,
lint,
serve,
build,
start
}
(3)启动
"scripts": {
// yarn clean ---->清除dist文件夹
"clean":"grunt clean",
// yarn compile ---->编译
"compile":"grunt compile",
// yarn serve---->启动编译后的服务
"serve": "gulp serve",
// yarn build---->打包
"build": "gulp build",
// yarn start---->启动打包后的服务
"start": "gulp start"
},