简答题
1、谈谈你对工程化的初步认识,结合你之前遇到过的问题说出三个以上工程化能够解决问题或者带来的价值。
对工程化的理解:
工程化是遵循开发流程、技术、工具、经验等规范化、标准化,其主要目的为了提高效率和降低成本,即提高开发过程中的开发效率,减少不必要的重复工作时间;工程化不等于某种工具,一切以提高效率、降低成本和质量保证为目的的手段都称之为工程化;工程化可以主要从模块化、组件化、规范化、自动化四个方面思考
解决的问题:
- 压缩图片大小,使请求图片时间降低
- 合并代码,减少项目整体体积
- 代码整体编译转译
带来的价值
- 无法使用模块化/组件化组织代码;
- 代码风格统一,使得项目多人协作开发,质量得以保证;
- 部分功能需要等待后端服务接口完成以后才可以进行开发;
- 重复的机械工作,比如部署上线前需要手动压缩代码及资源文件,部署过程需要手动上传代码到服务器。
2、你认为脚手架除了为我们创建项目结构,还有什么更深的意义?
脚手架的本质作用除了为我们创建项目结构,还为我们提供了项目规范和约定。脚手架创建的项目包含相同的组织结构、相同的开发范式、相同的模块依赖、相同的工程配置以及相同的基础代码。脚手架作为一种创建项目初始文件的工具被广泛地应用于新项目或迭代初始阶段。使用工具代替人工操作能够避免人为失误引起的低级错误,同时结合整体前端工程化方案,快速生成功能模块配置、自动安装依赖等,优化了时间成本。在公司中使用同一套脚手架工具创建的项目,使得项目成员更换时,能够马上上手,提高开发效率。
编程题
1、概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具
实现:
-
创建一个新的仓库目录: test-cli;
-
通过yarn init --yes 初始化项目生成packag.json文件
-
创建cli.js文件,将路径添加到pageage.json中,bin字段对应的值
-
通过yarn add ejs yeoman-generator --dev 安装依赖
{ "name": "test-cli", "version": "1.0.0", "bin": "cli.js", "main": "index.js", "license": "MIT", "dependencies": { "ejs": "^3.1.5", "yeoman-generator": "^4.11.0" } }
5、编写 cli.js逻辑内容
#!/usr/bin/env node
// 脚手架的工作过程
// 1、通过命令行交互询问用户问题
// 2、根据用户回答的结果生成文件
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const ejs = require('ejs')
inquirer.prompt([
{
type: 'input',
name: 'title',
message: 'Project name?'
},
{
type: 'input',
name: 'name',
message: 'your name?'
}
]).then(answer => {
// console.log(answer)
// 根据用户的回答生成文件
// 模板目录
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), answer, (err, result) => {
if( err) throw err
fs.writeFileSync(path.join(destDir, file), result)
})
})
})
})
6、创建html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
</head>
<body>
<h1><%= name %></h1>
</body>
</html>
7、通过 yarn link 命令将cli程序link到全局
8、命令行执行test-cli, 根据提示输入对应的name名称,生成模板inde.html文件
2、尝试使用 Gulp 完成项目的自动化构建
const { src, dest, parallel, series, watch } = require("gulp")
const browserSync = require("browser-sync")
const bs = browserSync.create()
const del = require("del")
const plugins = require("gulp-load-plugins")() // gulp-load-plugins插件方法
// 定义清除任务
const clean = () => {
return del(["dist"])
}
// 定义css任务
const style = () => {
return src("src/assets/styles/*.scss", { base: "src" })
.pipe(plugins.sass({ outputStyle: "expanded" }))
.pipe(dest("dist"))
.pipe(bs.reload({stream: true}))
}
// 定义js任务
const script = () => {
return src("src/assets/scripts/*.js", { base: "src" })
.pipe(plugins.babel({ presets: ["@babel/preset-env"] }))
.pipe(dest("dist"))
.pipe(bs.reload({stream: true}))
}
// 定义HTML任务
const html = () => {
return src("src/**/*.html", { base: "src" })
.pipe(plugins.swig())
.pipe(dest("dist"))
.pipe(bs.reload({stream: true}))
}
定义image图片任务
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"))
}
// useref 可以将html 中引用的css js 问价压缩合并,减少请求
const useref = () => {
return src('dist/*.html', {base: 'dist'})
.pipe(plugins.useref({searchPath: ['dist', '.']}))
.pipe(plugins.if(/\.js$/, plugins.uglify()))
.pipe(plugins.if(/\.css$/, plugins.cleanCss()))
.pipe(plugins.if(/\.html$/, plugins.htmlmin({
collapseWhitespace: true,
removeComments: true,
minifyCss: true, // 压缩页面css
minifyJs: true // 压缩页面js
})))
.pipe(dest('release')) // 使用release 从 dist文件读取编写到release
}
// 定义服务器
// 通过browserSync 设置服务器,
// watch api 监听文件的变化
const serve = () => {
watch("src/assets/styles/*.scss", style)
watch("src/assets/styles/*.scss", script)
watch("src/**/*.html", html)
// 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: 2020,
// open: false,
// files: "dist/**", // 同理也可以通过browserSync.reload 方法加载
server: {
baseDir: ["dist", "src", "public"],
routes: {
"/node_modules": "node_modules",
},
},
})
}
const compile = parallel(style, script, html)
// 上线之前
const build = series(clean, parallel(compile, image, font, extra))
const dev = series(parallel(compile), serve)
module.exports = {
clean,
compile,
build,
dev,
useref
}
/**
* yarn clean 清除dist文件
* yarn compile 执行编译
* yarn build 文件构建上线
* yarn dev 本地运行
* yarn useref 合并压缩文件
*/
pageage.json文件
{
"name": "pages-boilerplate",
"version": "0.1.0",
"private": true,
"description": "Always a pleasure scaffolding your awesome static sites.",
"keywords": [
"pages-boilerplate",
"boilerplate",
"pages",
"zce"
],
"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": "zce",
"email": "w@zce.me",
"url": "https://zce.me"
},
"scripts": {
"clean": "gulp clean",
"lint": "gulp lint",
"serve": "gulp serve",
"build": "gulp build",
"start": "gulp start",
"deploy": "gulp deploy --production"
},
"browserslist": [
"last 1 version",
"> 1%",
"maintained node versions",
"not dead"
],
"dependencies": {
"bootstrap": "4.4.1",
"gulp": "^4.0.2",
"gulp-imagemin": "^7.1.0",
"gulp-useref": "^4.0.1",
"jquery": "3.4.1",
"popper.js": "1.16.1"
},
"devDependencies": {
"@babel/core": "^7.11.4",
"@babel/preset-env": "^7.11.0",
"browser-sync": "^2.26.12",
"del": "^5.1.0",
"gulp-babel": "^8.0.0",
"gulp-clean-css": "^4.3.0",
"gulp-htmlmin": "^5.0.1",
"gulp-if": "^3.0.0",
"gulp-load-plugins": "^2.0.4",
"gulp-sass": "^4.1.0",
"gulp-swig": "^0.9.1",
"gulp-uglify": "^3.0.2"
},
"engines": {
"node": ">=6"
}
}