2-1作业参考答案
简答题
1、谈谈你对工程化的初步认识,结合你之前遇到过的问题说出三个以上工程化能够解决问题或者带来的价值。
答案:工程化是根据业务特点,将前端开发流程规范化,标准化,它包括了开发流程,技术选型,代码规范,构建发布等,用于提升前端工程师的开发效率和代码质量。
- 制定各项规范,编码规范,开发流程规范,前后端接口规范等等
- 使用版本控制工具 git,commit描述规范
- 使用合适前端技术和框架,提高生产效率,降低维护难度,采用模块化,组件化,数据分离等
- 代码可测试,单元测试,端到端测试等
- 开发部署自动化
2、你认为脚手架除了为我们创建项目结构,还有什么更深的意义?
- 减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。
- 可以根据交互动态生成项目结构和配置文件。
- 多人协作更为方便,不需要把文件传来传去。
编程题
1、概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具。
说明:实现了创建不同主题的,选择不同内容的静态应用
首先创建目录 初始化 yarn init 创建出 package.json
-
在package.json中 输入 bin入口
{ //... "bin": "lib.js", //... }
-
在根目录创建 lib.js文件 添加bin 入口标识
#!/usr/bin/env node //...
引入inquirer 模块 创建用户与命令行交互的工具 编写所需问题及字段
创建模板目录templates 将项目文件导入到目录中
引入ejs模块 结合所需功能问题变量 改写 templates 下项目文件 达到所需功能
在inquirer回调中 结合nodejs 读写功能 和ejs 模块将问题变量 重写到项目中
然后发布到npm上
//cli.js 参考
#!/usr/bin/env 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?'
},
{
type: 'list',
name: 'theme',
message: 'Select the theme color',
choices: ['Dark', 'Light'],
filter: function (val) {
return val.toLowerCase();
},
},
{
type: 'checkbox',
message: 'Select what to include',
name: 'content',
choices: [
{
name: 'Header',
},
{
name: 'Body',
},
{
name: 'Footer',
},
],
validate: function (answer) {
if (answer.length < 1) {
return 'You must choose at least one content.';
}
return true;
},
},
])
.then(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)
})
})
})
})
2、尝试使用 Gulp 完成项目的自动化构建
查找 gulp 插件:https://gulpjs.com/plugins/
根据项目结构分析得出前端静态应用
亮点: stylelint eslint postcss autoprefixer 实现 --fix
根据README 结合 package.json 得知gulp 需要实现的命令 如下:
yarn lint
yarn compile
yarn serve
yarn build
yarn start
yarn deploy
yarn clean
//并能实现一下 扩展参数命令
yarn serve --port 5210 --open
-
根据具体实现功能分布拆解 主要原理建立通道 分步处理
clean: del
compile:拆分为 style script page 同时执行
style:在处理sass的基础上 引入gulp-postcss 处理 autoprefixer, 后期可扩展更多postcss功能
script: 处理js 引入 gulp-babel 并 @babel/preset-env 来处理 es6+ 功能
page: 查看html文件为swig 文件,并且layouts,partials 文件夹下的为非入口文件 所以在src 添加ignore 排除这两个目录下的文件 在进行处理
serve:拆分为 compile devServe 首先生成 temp内容 然后开启开发服务器 监听变化 显示变化
devServe:将生成在 temp 目录下的文件 首先用watch 监听文件变化css js html 进行实时监听,实时编译。 browserSync 开启静态服务 未查询到的文件 可通过 路由处理 及 文件来源查找进行处理
build:拆分为clean 及重新打包 (先 compile 后 useref )在打包静态资源类 image,font,extra
useref: 在temp下 根据 html 注释 解析文件 将资源文件css 和 js 打包 根据引用资源 在 压缩js css html 生成在 dist 目录下
image,font:经过 imagemin 压缩处理 生成在dist目录下
extra:直接拷贝到dist目录下
start:拆分为**build **及 prodServe 处理
prodServe: 将生成的 dist 目录下的文件 通过 browserSync 开启静态服务器
deploy:拆分为 build 及 publish 处理
publish:将 生成的 dist 目录下的文件 通过gulp-gh-pages 插件 进行处理 发布到 gh-pages分支下
lint: 拆分为 stylesLint 及 scriptLint 处理 又添加了 gulp lint --fix 添加修复功能
styleLint: 通过gulp-postcss 结合相应 stylelint 插件 在根目录下.stylelintrc.json 文件引入 stylelint-config-sass-guidelines 对sass 文件进行文件 lint
scriptLint: 通过gulp-eslint 在根目录下.eslintrc.js 引入 eslint-config-airbnb-base 进行 强校验 保证代码质量
--production --port 5020 --open --fix 等 可通过 process.argv 获取 查询到 minimist 可进行相应处理 处理结果 可以直接拿到
--production 判断是否存在 存在可生成 js,css sourcemaps 文件
项目配置化,变量统一化, 可读取pages.config.js文件 可填写相应配置项 及 data数据
//gulpfile.js 仅做参考
const {
src, dest, parallel, series, watch,
} = require('gulp');
const del = require('del');
const browserSync = require('browser-sync');
const loadPlugins = require('gulp-load-plugins');
const autoprefixer = require('autoprefixer');
const stylelint = require('stylelint');
const scss = require('postcss-scss');
const reporter = require('postcss-reporter');
const minimist = require('minimist');
const plugins = loadPlugins();
const bs = browserSync.create();
const cwd = process.cwd();
const args = minimist(process.argv.slice(2));
const isProd = process.env.NODE_ENV ? process.env.NODE_ENV === 'production' : args.production || args.prod || false;
const bsInit = {
notify: false,
port: args.port || 2080,
open: args.open || false,
};
let config = {
build: {
src: 'src',
dist: 'dist',
temp: 'temp',
public: 'public',
paths: {
styles: 'assets/styles/**/*.scss',
scripts: 'assets/scripts/**/*.js',
pages: '**/*.html',
images: 'assets/images/**/*.{jpg,jpeg,png,gif,svg}',
fonts: 'assets/fonts/**/*.{eot,svg,ttf,woff,woff2}',
},
},
};
try {
const loadConfig = require(`${cwd}/pages.config.js`);
config = { ...config, ...loadConfig };
} catch (e) { }
const clean = () => del([config.build.dist, config.build.temp]);
const style = () => src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src, sourcemaps: !isProd })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(plugins.postcss([
autoprefixer(),
]))
.pipe(dest(config.build.temp, { sourcemaps: '.' }))
.pipe(bs.reload({ stream: true }));
const stylesLint = () => src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.postcss([
stylelint({ fix: args.fix }),
reporter(),
], { syntax: scss }));
const script = () => src(config.build.paths.scripts, {
base: config.build.src, cwd: config.build.src, sourcemaps: !isProd,
})
.pipe(plugins.babel({ presets: [require('@babel/preset-env')] }))
.pipe(dest(config.build.temp, { sourcemaps: '.' }))
.pipe(bs.reload({ stream: true }));
const scriptsLint = () => src(config.build.paths.scripts, {
base: config.build.src, cwd: config.build.src,
})
.pipe(plugins.eslint({ fix: args.fix }))
.pipe(plugins.eslint.format())
.pipe(plugins.eslint.failAfterError());
const page = () => src(config.build.paths.pages, {
base: config.build.src, cwd: config.build.src, ignore: ['{layouts,partials}/**'],
})
.pipe(plugins.swig({ data: config.data, defaults: { cache: false } }))
.pipe(dest(config.build.temp))
.pipe(bs.reload({ stream: true }));
const image = () => src(config.build.paths.images, {
base: config.build.src, cwd: config.build.src,
})
.pipe(plugins.imagemin())
.pipe(dest(config.build.dist));
const font = () => src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src })
.pipe(plugins.imagemin())
.pipe(dest(config.build.dist));
const extra = () => src('**', { base: config.build.public, cwd: config.build.public })
.pipe(dest(config.build.dist));
const devServe = () => {
watch(config.build.paths.styles, { cwd: config.build.src }, style);
watch(config.build.paths.scripts, { cwd: config.build.src }, script);
watch(config.build.paths.pages, { cwd: config.build.src }, page);
watch([
config.build.paths.images,
config.build.paths.fonts,
], { cwd: config.build.src }, bs.reload);
watch('**', { cwd: config.build.public }, bs.reload);
bs.init({
...bsInit,
server: {
baseDir: [config.build.temp, config.build.dist, config.build.public, config.build.src],
routes: {
'/node_modules': 'node_modules',
},
},
});
};
const prodServe = () => {
bs.init({
...bsInit,
server: {
baseDir: config.build.dist,
},
});
};
const useref = () => src(
config.build.paths.pages,
{ base: config.build.temp, cwd: config.build.temp },
)
.pipe(plugins.useref({ searchPath: [config.build.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(config.build.dist));
const publish = () => src('**', { base: config.build.dist, cwd: config.build.dist })
// .pipe(plugins.gzip())
.pipe(plugins.ghPages());
const lint = parallel(stylesLint, scriptsLint);
const compile = parallel(style, script, page);
// 上线之前执行的任务
const build = series(
clean,
parallel(
series(compile, useref),
image,
font,
extra,
),
);
const serve = series(compile, devServe);
const start = series(build, prodServe);
const deploy = series(build, publish);
module.exports = {
clean,
lint,
compile,
serve,
build,
start,
deploy,
};
3、使用 Grunt 完成项目的自动化构建
说明:
查找 grunt 插件:https://www.gruntjs.net/plugins
思路大体跟gulp一致 首先查找对应功能的插件 根据插件文档 编写插件信息
-
根据grunt文件管理 可以拆分 cwd src dest ext 等 精确控制
{ expand: true, cwd: config.build.src, src: [config.build.paths.styles], dest: config.build.temp, ext: '.css', }
grunt-usemin 代替 gulp-useref
grunt-browser-sync grunt-contrib-copy
watch 用到插件为 grunt-contrib-watch gulp则是本身功能
copy 用到插件为grunt-contrib-copy gulp则是本身功能
del 可使用 del.sync 同步执行
处理 swig 用到 grunt-html-template 插件
```javascript
//gruntfile.js 参考配置
const path = require('path');
const sass = require('sass');
const del = require('del');
const browserSync = require('browser-sync');
const loadGruntTasks = require('load-grunt-tasks');
const autoprefixer = require('autoprefixer');
const stylelint = require('stylelint');
const scss = require('postcss-scss');
const reporter = require('postcss-reporter');
const minimist = require('minimist');
const bs = browserSync.create();
const cwd = process.cwd();
const args = minimist(process.argv.slice(2));
const isProd = process.env.NODE_ENV ? process.env.NODE_ENV === 'production' : args.production || args.prod || false;
const bsInit = {
notify: false,
port: args.port || 2080,
open: args.open || false,
};
let config = {
build: {
src: 'src',
dist: 'dist',
temp: 'temp',
public: 'public',
paths: {
styles: 'assets/styles//.scss',
scripts: 'assets/scripts//.js',
pages: '/.html',
images: 'assets/images//.{jpg,jpeg,png,gif,svg}',
fonts: 'assets/fonts/*/.{eot,svg,ttf,woff,woff2}',
},
},
};
try {
const loadConfig = require(${cwd}/pages.config.js
);
config = { ...config, ...loadConfig };
} catch (e) { }
module.exports = (grunt) => {
grunt.initConfig({
sass: {
options: {
sourceMap: !isProd,
implementation: sass,
outputStyle: 'expanded',
},
main: {
expand: true,
cwd: config.build.src,
src: [config.build.paths.styles],
dest: config.build.temp,
ext: '.css',
},
},
postcss: {
main: {
options: {
processors: [
autoprefixer(),
],
},
expand: true,
cwd: config.build.temp,
src: ['assets/styles/*/.css'],
dest: config.build.temp,
},
lint: {
options: {
processors: [
stylelint({ fix: args.fix }),
reporter(),
],
},
src: `${path.join(config.build.src, config.build.paths.styles)}`,
},
},
eslint: {
options: {
fix: args.fix,
},
main: `${path.join(config.build.src, config.build.paths.scripts)}`,
},
babel: {
options: {
sourceMap: !isProd,
presets: ['@babel/preset-env'],
},
main: {
expand: true,
cwd: config.build.src,
src: [config.build.paths.scripts],
dest: config.build.temp,
ext: '.js',
},
},
html_template: {
options: {
cache: false,
locals: config.data,
},
main: {
expand: true,
cwd: config.build.src,
src: [config.build.paths.pages, '!layouts/**', '!partials/**'],
dest: config.build.temp,
ext: '.html',
},
},
imagemin: {
image: {
expand: true,
cwd: config.build.src,
src: [config.build.paths.images],
dest: config.build.dist,
},
font: {
expand: true,
cwd: config.build.src,
src: [config.build.paths.fonts],
dest: config.build.dist,
},
},
copy: {
main: {
expand: true,
cwd: config.build.public,
src: ['**'],
dest: config.build.dist,
},
html: {
expand: true,
cwd: config.build.temp,
src: [config.build.paths.pages],
dest: config.build.dist,
},
},
useminPrepare: {
main: {
expand: true,
cwd: config.build.temp,
src: [config.build.paths.pages],
},
options: {
dest: config.build.dist,
root: [config.build.temp, '.', '..'],
},
},
usemin: {
main: {
expand: true,
cwd: config.build.dist,
src: [config.build.paths.pages],
},
options: {
},
},
'gh-pages': {
options: {
base: config.build.dist,
branch: 'gh-pages-grunt',
},
main: ['**'],
},
watch: {
script: {
files: [`${path.join(config.build.src, config.build.paths.scripts)}`],
tasks: ['babel'],
},
style: {
files: [`${path.join(config.build.src, config.build.paths.styles)}`],
tasks: ['style'],
},
page: {
files: [`${path.join(config.build.src, config.build.paths.pages)}`],
tasks: ['html_template'],
},
},
browserSync: {
dev: {
bsFiles: {
src: [
config.build.temp,
config.build.dist,
],
},
options: {
...bsInit,
watchTask: true,
server: {
baseDir: [config.build.temp, config.build.dist, config.build.public, config.build.src],
routes: {
'/node_modules': 'node_modules',
},
},
},
},
prod: {
bsFiles: {
src: config.build.dist,
},
options: {
...bsInit,
server: {
baseDir: config.build.dist,
},
},
},
},
});
loadGruntTasks(grunt);
grunt.registerTask('reload', () => {
bs.reload();
});
grunt.registerTask('stylesLint', []);
grunt.registerTask('scriptsLint', []);
grunt.registerTask('clean', () => {
del.sync([config.build.dist, config.build.temp, '.tmp']);
});
grunt.registerTask('style', ['sass', 'postcss:main']);
grunt.registerTask('compile', ['style', 'babel', 'html_template']);
grunt.registerTask('build', [
'clean',
'compile',
'copy',
'useminPrepare',
'concat:generated',
'cssmin:generated',
'uglify:generated',
'usemin',
'imagemin',
]);
grunt.registerTask('serve', ['compile', 'browserSync:dev', 'watch']);
grunt.registerTask('start', ['build', 'browserSync:prod']);
grunt.registerTask('deploy', ['build', 'gh-pages']);
grunt.registerTask('lint', ['postcss:lint', 'eslint']);
};