简答题

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

工程化是根据业务特点,将前端开发流程规范化,标准化,它包括了开发流程,技术选型,代码规范,构建发布等,用于提升前端工程师的开发效率和代码质量。 

1. 制定各项规范,编码规范,开发流程规范,前后端接口规范等等

2. 使用版本控制工具 git,commit描述规范

3. 使用合适前端技术和框架,提高生产效率,降低维护难度,采用模块化,组件化,数据分离等

4. 代码可测试,单元测试,端到端测试等

5. 开发部署自动化

  • 你认为脚手架除了为我们创建项目结构,还有什么更深的意义? 
  1. 减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件
  2. 可以根据交互动态生成项目结构和配置文件
  3. 多人协作更为方便,不需要把文件传来传去 

编程题 

  •  概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具
  1. 首先创建目录 初始化 yarn init 创建出 package.json
  2. 在package.json中 输入 bin入口 
      {
        //...
        "bin": "lib.js",
        //...
      }

     

  3. 在根目录创建 lib.js文件 添加bin 入口标识

    #!/usr/bin/env node
    
    //fs模块是Node.js的一个核心模块,专门用来操作系统中的文件,常用的操作方式是对文件的读取和写入使用require('fs')载入fs模块,模块中所有方法都有同步和异步两种形式
    //参考网址:https://blog.csdn.net/qq_45040919/article/details/109751579
    const fs = require('fs')
    
    //一个路径模块,包含路径解析,路径截个、合并,获取绝对、相对路径等等
    //参考网址:https://blog.csdn.net/qq_34629352/article/details/56017209
    const path = require('path')
    
    //一个使用界面进行交互的命令行集合
    //参考网址:https://blog.csdn.net/weixin_30433075/article/details/99067266
    const inquirer = require('inquirer')
    
    //EJS 是后台模板,可以把我们数据库和文件读取的数据显示到 Html 页面上面(解析页面视图模板)。它是一个第三方模块,需要通过 npm 或 yarn 安装
    //参考网址:https://blog.csdn.net/houyanhua1/article/details/79451965
    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) => { //readdir()异步的方式读取目录中的子文件和目录
        if (err) throw err
        files.forEach(file => { //path.join()路径结合、合并,路径最后不会带目录分隔符
          ejs.renderFile(path.join(tmplDir, file), anwsers, (err, result) => {
            if (err) throw err
    
            fs.writeFileSync(path.join(destDir, file), result)
          })
        })
      })
    })

     

  4. 引入inquirer 模块 创建用户与命令行交互的工具 编写所需问题及字段, yarn add inquirer --dev

  5. 创建模板目录templates 将项目文件导入到目录中

  6. 引入ejs模块 结合所需功能问题变量 改写 templates 下项目文件 达到所需功能,yarn add ejs --dev

  7. 在inquirer回调中 结合nodejs 读写功能 和ejs 模块将问题变量 重写到项目中,yarn add inquirer --dev

  8. 然后发布到npm上

  •  尝试使用 Gulp 完成项目的自动化构建(查找 gulp 插件:https://gulpjs.com/plugins/)
  1. 根据项目结构分析得出前端静态应用 
  2. 亮点: **stylelint** **eslint** **postcss** **autoprefixer** 实现 **--fix** 
  3. 根据README 结合 package.json 得知gulp 需要实现的命令 如下:
      yarn lint
      yarn compile
      yarn serve
      yarn build
      yarn start
      yarn deploy
      yarn clean
      //并能实现一下 扩展参数命令
      yarn serve --port 5210 --open

     

  4. 根据具体实现功能分布拆解 主要原理建立通道 分步处理

      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 进行 强校验 保证代码质量

  5. production --port 5020 --open --fix 等 可通过 process.argv 获取 查询到 minimist 可进行相应处理 处理结果 可以直接拿到

  6. production 判断是否存在 存在可生成 js,css sourcemaps 文件

  7. 项目配置化,变量统一化, 可读取pages.config.js文件 可填写相应配置项 及 data数据

  8. gulpfile.js 参考配置

//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,
};
  •  使用 Grunt 完成项目的自动化构建(查找 grunt 插件:https://www.gruntjs.net/plugins)
  1. 思路大体跟gulp一致 首先查找对应功能的插件 根据插件文档 编写插件信息
  2.  根据grunt文件管理 可以拆分 cwd src dest ext 等 精确控制
     {
        expand: true,
        cwd: config.build.src,
        src: [config.build.paths.styles],
        dest: config.build.temp,
        ext: '.css',
      }

     

  3. grunt-usemin 代替 gulp-useref

  4. grunt-browser-sync grunt-contrib-copy

  5. watch 用到插件为 grunt-contrib-watch gulp则是本身功能

  6. copy 用到插件为grunt-contrib-copy gulp则是本身功能

  7. del 可使用 del.sync 同步执行

  8. 处理 swig 用到 grunt-html-template 插件

  9. gruntfile.js 参考配置
     

    //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']);
    };

     

 

Logo

一站式 AI 云服务平台

更多推荐