开发脚手架及封装自动化构建工作流

简答题

1、谈谈你对工程化的初步认识,结合你之前遇到过的问题说出三个以上工程化能够解决问题或者带来的价值。

  1. 在前端工作期间,认为前端工程化是遵循一定的标准和规范,通过工具来提高工作效率和减低开发成本的一种方式方法。总而言之,一切以提高效率、缩减成本、质量保证为目的的手段都属于前端工程化范畴,一切重复的工作都应该被自动化,而不是指使用某种工具,是一个成熟的规范化的流程。
  2. 实际开发过程中能够解决的问题:
  3. 1.各自都有自己的编码习惯,那么使用模板规范写法上的一些格式,可以保证编码风格统一、编码质量上有一定保证。各成员开发上协调性更好,代码阅读上,格式整体趋于一致,更易于修改。
  4. 2.新开一个项目时,以日常Vue-Cli为例,新创建一个项目目录,在生成期间安装运行、开发依赖需要一个个安装,工程化可以免除这些手动机械化的操作,生成一套完善的项目。
  5. 3.以往开发部分页面需要等待后端接口完成,现在分离后可以同步开发,等待联调即可。解决了开发上的时间问题。

2、你认为脚手架除了为我们创建项目结构,还有什么更深的意义?

  1. 应该是从公司或企业长远的发展来看,给开发者制定一种规范和约束,使得开发工作人员使用相同的组织架构、编码风格、甚至编码思想上能趋于一致,便于当下的开发和维护,更为了日后长远的代码维护,高质量高标准的代码,能减少后期开发人员的学习成本和维护成本。

编程题

1、概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具

脚手架的实现过程

  1. 打开cmd,创建工作目录,使用yarn init初始化package.json文件,用过vscode打开 ```javascript mkdir easy-demo cd easy-demo yarn init

会自动询问一些预设问题,会生成一个项目结构


code .

  1. 2. package.json中添加bin字段,用于指定cli应用的入口文件
  2. ```javascript
  3. {
  4. "name": "easy-demo",
  5. "version": "1.0.0",
  6. "description": "一步一步实现脚手架",
  7. "main": "index.js",
  8. "author": "peiyp",
  9. "license": "MIT",
  10. "bin": "cli.js"
  11. }
  1. 添加cli.js文件,并添加文件头

    1. #!/usr/bin/env node
  2. 在node中安装inquirer模块

    1. yarn add inquirer
  3. 在node中安装模板引擎ejs

    1. yarn add ejs
  4. 在cli.js文件中载入inquirer模块 ```javascript

    !/usr/bin/env node

const fs = require(‘fs’) const path = require(‘path’) const inquirer = require(‘inquirer’) const ejs = require(‘ejs’)

// prompt()发起命令行的询问 可接收一个数组参数 inquirer.prompt([ { // 每一个成员即为一个问题 type: ‘input’, // 问题输入方式 name: ‘name’, // 问题返回值的键 message: ‘Project name?’ // 终端给出的提示 } ]) .then(anwsers => { // 根据用户回答的结果生成文件

  1. // 模板目录
  2. const tmplDir = path.join(__dirname, 'templates')
  3. // 目标目录
  4. const destDir = process.cwd()
  5. // 将模板下的文件全部转换到目标目录
  6. fs.readdir(tmplDir, (err, files) => {
  7. if (err) throw err
  8. files.forEach(file => {
  9. // 通过模板引擎提供的rendFile()渲染文件
  10. // 参数 文件的绝对路径 模板引擎工作的数据上下文 回调函数
  11. ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => {
  12. if (err) throw err
  13. // 将结果写入目标文件路径
  14. fs.writeFileSync(path.join(destDir, file), result)
  15. })
  16. })
  17. })
  18. })
  1. 7. 将此模块连接到全局,使其成为一个全局模块包,本地的时候可以全局引使用
  2. ```javascript
  3. yarn link
  1. 测试=>创建一个新的目录

    1. mkdir doDemo
    2. cd .\doDemo
    3. yarn link easy-demo

    2、尝试使用 Gulp 完成项目的自动化构建

    使用pages-boilerplate文件包开发

  2. 在vscode中打开项目,安装gulp

    1. yarn add gulp --dev
  3. 在项目根目录中添加gulpfile.js入口文件

  4. 在项目中添加需要使用到的插件 ```javascript // 使用 gulp-load-plugins 插件批量引入package.json文件中的依赖项工具,从而不必在gulfile.js中手动引入每个gulp插件 yarn add gulp-load-plugins —dev

// 使用 gulp-sass 插件编译 scss 文件,将 scss 转换为 css;后续我们将使用 gulp-clean-css 插件,对 css 文件进行压缩 yarn add gulp-sass —dev yarn add gulp-clean-css —dev

// 使用 gulp-babel、 @babel/core、 @babel/preset-env 插件编译 js 文件,将 es6 转换为 es5;后续我们将使用 gulp-uglify 插件,对 js 文件进行压缩 yarn add gulp-babel @babel/core @babel/preset-env —dev yarn add gulp-uglify —dev

// 使用 gulp-swig 插件编译 html 文件,并将数据对象中的变量注入模板,设置不缓存页面;后续我们将使用 gulp-htmlmin 插件,对 html 文件进行压缩 yarn add gulp-swig —dev yarn add gulp-htmlmin —dev

// 使用 gulp-imagemin 插件将图片文件和字体文件进行压缩 yarn add gulp-imagemin —dev

// del插件将原先编译后的文件目录删除 yarn add del —dev

// browser-sync 插件使浏览器热更新,提高开发效率 yarn add browser-sync —dev

// gulp-useref 插件可以将 HTML 引用的多个 CSS 和 JS 合并起来,减小依赖的文件个数,从而减少浏览器发起的请求次数 yarn add gulp-useref —dev

// gulp-if 插件来判断读取流文件类型,并压缩对应文件 yarn add gulp-if —dev

  1. 4. gulpfile.js中定义构建任务
  2. ```javascript
  3. // 实现这个项目的构建任务
  4. const { src, dest, parallel, series, watch } = require('gulp')
  5. const del = require('del')
  6. const browserSync = require('browser-sync')
  7. const loadPlugins = require('gulp-load-plugins')
  8. const plugins = loadPlugins()
  9. const bs = browserSync.create()
  10. const data = {
  11. menus: [
  12. {
  13. name: 'Home',
  14. icon: 'aperture',
  15. link: 'index.html'
  16. },
  17. {
  18. name: 'Features',
  19. link: 'features.html'
  20. },
  21. {
  22. name: 'About',
  23. link: 'about.html'
  24. },
  25. {
  26. name: 'Contact',
  27. link: '#',
  28. children: [
  29. {
  30. name: 'Twitter',
  31. link: 'https://twitter.com/w_zce'
  32. },
  33. {
  34. name: 'About',
  35. link: 'https://weibo.com/zceme'
  36. },
  37. {
  38. name: 'divider'
  39. },
  40. {
  41. name: 'About',
  42. link: 'https://github.com/zce'
  43. }
  44. ]
  45. }
  46. ],
  47. pkg: require('./package.json'),
  48. date: new Date()
  49. }
  50. const clean = () => {
  51. return del(['dist', 'temp'])
  52. }
  53. const style = () => {
  54. // 通过src的选项参数base来确定转换过后的基准路径
  55. return src('src/assets/styles/*.scss', { base: 'src' })
  56. .pipe(plugins.sass({ outputStyle: 'expanded' })) // 完全展开构建后的代码
  57. .pipe(dest('temp'))
  58. }
  59. const script = () => {
  60. return src('src/assets/scripts/*.js', { base: 'src' })
  61. // 只是去唤醒babel/core这个模块当中的转换过程
  62. // babel作为一个平台不做任何事情,只是提供一个环境
  63. // presets 就是插件的集合
  64. .pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
  65. .pipe(dest('temp'))
  66. }
  67. const page = () => {
  68. return src('src/*.html', { base: 'src' })
  69. .pipe(plugins.swig({ data: data, defaults: { cache: false } })) // 编译html,并将数据对象中的变量注入模板,不缓存
  70. .pipe(dest('temp'))
  71. }
  72. const image = () => {
  73. return src('src/assets/images/**', { base: 'src' })
  74. .pipe(plugins.imagemin())
  75. .pipe(dest('dist'))
  76. }
  77. const font = () => {
  78. return src('src/assets/fonts/**', { base: 'src' })
  79. .pipe(plugins.imagemin())
  80. .pipe(dest('dist'))
  81. }
  82. const extra = () => {
  83. return src('public/**', { base: 'public' })
  84. .pipe(dest('dist'))
  85. }
  86. const serve = () => {
  87. watch('src/assets/styles/*.scss', style)
  88. watch('src/assets/scripts/*.js', script)
  89. watch('src/*.html', page)
  90. // watch('src/assets/images/**', image)
  91. // watch('src/assets/fonts/**', font)
  92. // watch('public/**', extra)
  93. watch([
  94. 'src/assets/images/**',
  95. 'src/assets/fonts/**',
  96. 'public/**'
  97. ], bs.reload)
  98. bs.init({
  99. notify: false, // 是否提示
  100. port: 2080, // 端口
  101. open: true, // 自动打开页面 默认true
  102. files: 'temp/**', // 启动后自动监听的文件
  103. server: {
  104. baseDir: ['temp', 'src', 'public'],
  105. routes: { // 优先于baseDir
  106. '/node_modules': 'node_modules'
  107. }
  108. }
  109. })
  110. }
  111. const useref = () => {
  112. return src('temp/*.html', { base: 'temp' })
  113. .pipe(plugins.useref({ searchPath: ['dist', '.'] }))
  114. // html js css三种流
  115. // 压缩js文件
  116. .pipe(plugins.if(/\.js$/, plugins.uglify()))
  117. // 压缩css文件
  118. .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
  119. // 压缩html文件
  120. .pipe(
  121. plugins.if(/\.html$/, plugins.htmlmin({ // 默认只压缩空白字符
  122. collapseWhitespace: true,
  123. minifyCSS: true,
  124. minifyJS: true
  125. })))
  126. .pipe(dest('dist'))
  127. }
  128. const compile = parallel(style, script, page)
  129. // 上线之前执行的任务
  130. const build = series(
  131. clean,
  132. parallel(
  133. series(compile, useref),
  134. image,
  135. font,
  136. extra
  137. )
  138. )
  139. const develop = series(compile, serve)
  140. module.exports = {
  141. clean,
  142. build,
  143. develop
  144. }
  1. 执行yarn gulp build获得dist和temp文件夹下的文件目录
  2. 执行yarn gulp develop 查看页面

yarn时碰到了问题 error An unexpected error occurred: “http://registry.cnpmjs.org/inquire: Hostname/IP does not match certificate’s altnames: Host: registry.cnpmjs.org. is not in the cert’s altnames: DNS:r.cnpmjs.org”. 解决方案:npm config set registry http://registry.npmjs.org/