大前端:练习题-开发脚手架及封装自动化构建工作流

一、简答题

1、谈谈你对工程化的初步认识,结合你之前遇到过的问题说出三个以上工程化能够解决问题或者带来的价值。
答案:工程化是根据业务特点,将前端开发流程规范化,标准化,它包括了开发流程,技术选型,代码规范,构建发布等,用于提升前端工程师的开发效率和代码质量。

  1. 制定代码规范,开发流程规范
  2. 代码可测试,单元测试,端到端测试等
  3. 开发部署自动化
  4. 文件自动编译打包,文件名随便生成解决了新版本上线缓存问题

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

  1. 减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。
  2. 可以根据交互动态生成项目结构和配置文件。
  3. 多人协作更为方便,不需要把文件传来传去。

三、编程题

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

  1. 初始化 yarn init -y 创建出 package.json
  2. 在package.json中 输入 bin入口
{
 //...
 "bin": "lib.js",
 //...
}
  1. 在根目录创建 lib.js文件 添加bin 入口标识
#!/usr/bin/env node

//...
  1. 引入inquirer 模块 创建用户与命令行交互的工具 编写所需问题及字段
  2. 创建模板目录templates 将项目文件导入到目录中
  3. 引入ejs模块 结合所需功能问题变量 改写 templates 下项目文件 达到所需功能
  4. 在inquirer回调中 结合nodejs 读写功能 和ejs 模块将问题变量 重写到项目中
  5. 然后发布到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 完成项目的自动化构建

  yarn lint
  yarn compile
  yarn serve
  yarn build
  yarn start
  yarn deploy
  yarn clean
  //并能实现一下 扩展参数命令
  yarn serve --port 5210 --open

读取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文件管理 可以拆分 cwd src dest ext 等 精确控制

{
  expand: true,
  cwd: config.build.src,
  src: [config.build.paths.styles],
  dest: config.build.temp,
  ext: '.css',
}

创建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']);
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值