## 简答题
**1、谈谈你对工程化的初步认识,结合你之前遇到过的问题说出三个以上工程化能够解决问题或者带来的价值。**
答: 工程化:是能够遵照一定的规范和标准【初始化项目,实现,自动编译,自动合并,自动压缩,自动打包,自动构建,自动部署等代替人手动实现】通过工具来提升效率的一种手段,和一切能提升前端开发效率,提高前端应用质量的手段和工具都是前端工程化。
解决问题:
1:提升开发效率
之前从零开发一个项目需要花费大量的时间去初始化项目,比如安装各种 npm 包、配置各种 Webpack 的 loader、配置热加载。如果碰到环境搭建不顺利的情况,还需要花费很长时间去排查问题,现在使用脚手架可以,只需要简单的一个初始化命令,便可以完成项目的初始化,效率大幅提升。
2: 降低大型项目的开发难度
2.1: 前端工程化中提倡模块化,组件化开发,
模块化将项目功能拆解,分成一个一个独立的模块,针对每个功能模块进行开发,使得开发难度下降
2.2: 模块化开发优点:项目在后期迭代中,由于各个模块是独立的,想要迭代某个功能,只需要调整该功能对应的模块就可以,不会影响其他的模块,使得项目风险可控
3: 使项目统一化,标准化,多人协作也可按照标准来实现项目的自动统一构建和统一部署
3.1: 前端工程化提倡可用完善的 流程规范 和 代码规范 来保证大型应用的质量和可维护性
比如通过eslint ,stylelint对代码格式进行自动校验
3.2: 同时基于Git工具来管理项目,使得开发者可以并行开发,提升开发效率
4: 解决,重复的机械工作
比如部署上线前需要手动压缩,打包,合并代码及资源文件,部署过程需要手动上传代码到服务器
解决的问题:
1.无法使用模块化/组件化组织代码
2.代码风格统一,使得项目多人协作开发,质量得以保证
3.部分功能需要等待后端服务接口完成以后才可以进行开发
4.重复的机械工作,比如部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器。
带来的价值:
1.压缩图片大小,使得请求图片时间降低
2.代码合并压缩,减少项目整体体积
**2、你认为脚手架除了为我们创建项目结构,还有什么更深的意义?**
答: 1: 提高效率:能够快速的搭建一个基础项目结构,快速上手。
2: 能够使得项目开发遵循制定的统一的规则,实现项目结构的规范化,标准化,以及代码风格的统一化,提高代码质量和利于后期维护【相同的项目结构,相同的模块依赖,相同的开发规范,相同的工程配置,相同的基础代码】
## 编程题
**1、概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具**
项目代码: 项目链接
1: 创建一个新的项目文件夹:sample-node-js
2: 初始化项目并生成package.json文件:npm init 或者 yarn init
3: 项目根目录下创建cli.js文件,用于指定 CLI 应用的入口文件,并在package.json中添加bin目录
{
"name": "sample-node",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": "bin.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "seven JI",
"license": "ISC",
"dependencies": {
"ejs": "^2.6.2",
"inquirer": "^8.1.1"
}
}
4: 编写cli.js前,准备工作
首先安装 inquirer( 一个用户与命令行交互的工具)
安装 ejs(一套简单的模板语言,可以将数据和模板合并然后生成 HTML 文本)
npm install inquirer --dev
npm install ejs --dev
安装完成后在 cli.js 引入这2个工具
const ejs = require('ejs')
const inquire = require('inquire')
5: cli.js 添加文件头
#!/usr/bin/env node
6: 在根目录下添加模版文件templates,里面包含index.html 和style.css文件
templates/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>
templates/styel.css
body {
margin: 0;
background-color: #f8f9fb;
}
7: cli.js 文件编写
#!/usr/bin/env node
// node cli 应用的入口文件必须要这样的开头
// 如果是Linux 或者macOS 系统下还需要修改此文件的读写权限为 755
// 具体通过 触摸到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: 'please input Project name'
}
])
.then(anwsers => {
console.log(anwsers)
// 根据用户回答输入结果生成文件
// 模版目录
templDir = path.join(__dirname, 'templates')
// 目标目录
const destDir = process.cwd()
// 将模版下的文件全部转换到目标目录
fs.readdir(templDir, (err, files) => {
if (err) throw err
files.forEach(file => {
console.log(file)
// 通过模版引擎渲染文件
ejs.renderFile(path.join(templDir, file), anwsers, (error, result) => {
if (err) throw err
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})
8: 运行sample-node-js,并根据提示输入相应的值,则会在项目根目录下创建由该模版生成的index.html 和 style.css文件
9: 或者 npm link /. yarn link关联到全局变量,
npm link
执行测试
mkdir test // 创建测试文件夹。
cd test // 切换文件夹
sample-node-js //执行脚手架
test 文件下创建由该模版生成的index.html 和 style.css文件
**2、尝试使用 Gulp 完成项目的自动化构建**
Gulp项目代码:Gulp项目链接
- 主要任务
gulp-sass 编译scss文件
gulp-babel 编译JS
gulp-imagemin 处理图片、 字体
拷贝静态资源
gulp-swig 处理HTML模板文件
browser-sync 搭建开发服务器
监听文件改变
gulp-useref gulp-if 文件引用处理
gulp-load-plugins 自动加载插件
- 组合任务
develop 用于开发环境
build 用于生产
const compile = parallel(style, script, page)
const build = series(clean, parallel(series(compile, useref), image, font, extra))
const develop = series(compile, serve)
1: 新建文件夹项目文件夹: pages-boilerplate
2: 初始化 项目,并生成package.json文件: npm init 或 yarn init
3: 安装gulp
npm install gulp
4: 安装所需插件
//將sass转化为浏览器可识别的css文件
yarn add gulp-sass sass -dev 或者
npm install gulp-sass sass -dev
//将ECMAScript新特性转化
yarn add gulp-babel @babel/core @babel/preset-env --dev
npm install gulp-babel @babel/core @babel/preset-env --dev
//html文件编译处理
yarn add gulp-swig --dev 或者
npm install gulp-swig --dev
// 压缩图片字体
yarn add gulp-imagemin --dev 或者
npm install gulp-imagemin --dev
// 启动服务
yarn add browser-sync --dev 或者
npm install browser-sync --dev
// 自动加载插件
yarn add gulp-load-plugins --dev 或者
npm install gulp-load-plugins --dev
// 清除文件
yarn add del --dev 或者
npm install del --dev
// 合并文件
yarn add gulp-useref --dev 或者
npm install gulp-useref --dev
// 判断
yarn add gulp-if --dev 或者
npm install gulp-if --dev
// 压缩js
yarn add gulp-uglify --dev 或者
npm install gulp-uglify --dev
// 压缩css
yarn add gulp-clean-css --dev 或者
npm install gulp-clean-css --dev
// 压缩html
yarn add gulp-htmlmin --dev 或者
npm install gulp-htmlmin --dev
5: 根目录下新建gulpfile.js
// 实现这个项目的构建任务
const { src, dest, series, parallel, watch } = require('gulp')
// const sass = require('gulp-sass')
// const babel = require('gulp-babel')
// const swig = require('gulp-swig')
// const imagemin = require('gulp-imagemin')
// const if = require('gulp-if')
// const useref = require('gulp-useref')
// const uglify = require('gulp-uglify')
// const htmlmin = require('gulp-htmlmin')
// const cleanCss = require('gulp-clean-css')
// 自动加载插件
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const del = require('del')
const bs = require('browser-sync').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()
}
// 1: 样式编译--css
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'))
}
// 2: 脚本编译---js
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('temp'))
}
// 3: 页面模版编译---html
const page = () => {
return src('src/*.html', { base: 'src' })
// catch: false 防止模版缓存导致页面不能及时刷新
.pipe(plugins.swig({ data: data, defaults: { catch: false } }))
.pipe(dest('temp'))
}
// 4: 图片和字体文件转换
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'))
}
// 5: 其他文件以及文件清除
const extra = () => {
return src('publicl/**', { base: 'public' })
.pipe(dest('dist'))
}
const clean = () => {
return del(['dist', 'temp'])
}
// 6:自动加载插件
/*
const loadPlugins = require('gulp-load-plugins')
const plugins =loadPlugins() // plugins变成一个拥有所有插件方法的集合方法,
如:
之前的需要引入 const sass = require('gulp-sass') 然后调用sass()
现在不需要引入sass,可直接调用 plugins.sass()
*/
// 7: 开发服务器
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, // 默认3000
// open: false, // 启动服务后是否打开浏览器,默认为true
// files: 'dist/**', // 监听那些文件的改变会自动更新到浏览器
server: {
baseDir: ['temp', 'src', 'public'],
routes: {
// 优于baseDir加载,配置路由镜像:加载的目录是node_modules文件
'/node_modules': 'node_modules'
}
}
})
}
// 8: useref: 将多个文件引用合并处理。 并 文件压缩
const useref = () => {
return src('temp/**', { base: 'temp' })
// 合并引用的文件到一个文件中
.pipe(plugins.useref({ searchPath: ['temp', '.']}))
.pipe(plugins.if(/\.js$/, plugins.uglify())) // 压缩js
.pipe(plugins.if(/\.css$/, plugins.cleanCss())) // 压缩css
.pipe(plugins.if(/.html$/, plugins.htmlmin({
removeComments: false, // 清除HTML注释
collapseWhitespace: true, // 压缩HTML折叠空格
minifyCSS: true, // 压缩页面CSS
minifyJS: true // 压缩页面JS
})))
.pipe(dest('dist')) // 将结果写入dist文件
}
// 9: 重新规划构建过程(不能写入和读取同一个文件且同时进行,会出现冲突),
// 我们先从src下面编译文件,放入中间文件temp,再将temp合并,压缩文件 最后放入dist
// 注意:图片。字体和其他文件不需要转换直接放入dist文件中
// 10: 将构建的命令添加到package.json的scripts里面 方便项目共同开发时,标准化启动和构建压缩打包命令
const compile = parallel(style, script, page)
const build = series(clean, parallel(series(compile, useref), image, font, extra))
const develop = series(compile, serve)
module.exports = {
image,
serve,
clean,
build,
develop
}
6: package.json
{
"name": "pages-boilerplate",
"version": "0.1.0",
"private": true,
"description": "Always a pleasure scaffolding your awesome static sites.",
"keywords": [
"pages-boilerplate",
"boilerplate",
"pages"
],
"homepage": "https://github.com/zce/pages-boilerplate#readme",
"bugs": {
"url": "https://github.com/zce/pages-boilerplate/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/zce/pages-boilerplate.git"
},
"license": "MIT",
"author": {
"name": "sevenJi"
},
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"develop": "gulp develop"
},
"browserslist": [
"last 1 version",
"> 1%",
"maintained node versions",
"not dead"
],
"dependencies": {
"@babel/core": "^7.7.7",
"@babel/preset-env": "^7.5.5",
"bootstrap": "4.4.1",
"browser-sync": "^2.26.7",
"del": "^5.1.0",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-clean-css": "^4.2.0",
"gulp-htmlmin": "^5.0.1",
"gulp-if": "^3.0.0",
"gulp-imagemin": "^6.1.0",
"gulp-load-plugins": "^2.0.2",
"gulp-sass": "^4.0.2",
"gulp-swig": "^0.9.1",
"gulp-uglify": "^3.0.2",
"gulp-useref": "^3.1.6",
"jquery": "3.4.1",
"popper.js": "1.16.1"
},
"devDependencies": {},
"engines": {
"node": ">=6"
}
}
7: 执行
npm run build / yarn build------->用于生产环境,会合并,压缩代码
npm run develop / yarn develop ------>用于开发环境,编译代码,启动服务