2-1作業(yè)參考答案

2-1作業(yè)參考答案

簡答題

1掌腰、談?wù)勀銓こ袒某醪秸J識,結(jié)合你之前遇到過的問題說出三個以上工程化能夠解決問題或者帶來的價值。

答案:工程化是根據(jù)業(yè)務(wù)特點丸冕,將前端開發(fā)流程規(guī)范化延赌,標準化除盏,它包括了開發(fā)流程,技術(shù)選型挫以,代碼規(guī)范者蠕,構(gòu)建發(fā)布等,用于提升前端工程師的開發(fā)效率和代碼質(zhì)量掐松。

  1. 制定各項規(guī)范踱侣,編碼規(guī)范,開發(fā)流程規(guī)范大磺,前后端接口規(guī)范等等
  2. 使用版本控制工具 git抡句,commit描述規(guī)范
  3. 使用合適前端技術(shù)和框架,提高生產(chǎn)效率杠愧,降低維護難度待榔,采用模塊化,組件化殴蹄,數(shù)據(jù)分離等
  4. 代碼可測試究抓,單元測試,端到端測試等
  5. 開發(fā)部署自動化
2袭灯、你認為腳手架除了為我們創(chuàng)建項目結(jié)構(gòu)刺下,還有什么更深的意義?
  • 減少重復(fù)性的工作稽荧,不需要復(fù)制其他項目再刪除無關(guān)代碼橘茉,或者從零創(chuàng)建一個項目和文件工腋。
  • 可以根據(jù)交互動態(tài)生成項目結(jié)構(gòu)和配置文件。
  • 多人協(xié)作更為方便畅卓,不需要把文件傳來傳去擅腰。

編程題

1、概述腳手架實現(xiàn)的過程翁潘,并使用 NodeJS 完成一個自定義的小型腳手架工具趁冈。

說明:實現(xiàn)了創(chuàng)建不同主題的,選擇不同內(nèi)容的靜態(tài)應(yīng)用

  • 首先創(chuàng)建目錄 初始化 yarn init 創(chuàng)建出 package.json

  • 在package.json中 輸入 bin入口

    {
      //...
      "bin": "lib.js",
      //...
    }
    
  • 在根目錄創(chuàng)建 lib.js文件 添加bin 入口標識

    #!/usr/bin/env node
    
    //...
    
  • 引入inquirer 模塊 創(chuàng)建用戶與命令行交互的工具 編寫所需問題及字段

  • 創(chuàng)建模板目錄templates 將項目文件導(dǎo)入到目錄中

  • 引入ejs模塊 結(jié)合所需功能問題變量 改寫 templates 下項目文件 達到所需功能

  • 在inquirer回調(diào)中 結(jié)合nodejs 讀寫功能 和ejs 模塊將問題變量 重寫到項目中

  • 然后發(fā)布到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 完成項目的自動化構(gòu)建

? 查找 gulp 插件:https://gulpjs.com/plugins/

  • 根據(jù)項目結(jié)構(gòu)分析得出前端靜態(tài)應(yīng)用

  • 亮點: stylelint eslint postcss autoprefixer 實現(xiàn) --fix

  • 根據(jù)README 結(jié)合 package.json 得知gulp 需要實現(xiàn)的命令 如下:

  yarn lint
  yarn compile
  yarn serve
  yarn build
  yarn start
  yarn deploy
  yarn clean
  //并能實現(xiàn)一下 擴展參數(shù)命令
  yarn serve --port 5210 --open
  • 根據(jù)具體實現(xiàn)功能分布拆解 主要原理建立通道 分步處理

    clean: del

    compile:拆分為 style script page 同時執(zhí)行

    style:在處理sass的基礎(chǔ)上 引入gulp-postcss 處理 autoprefixer, 后期可擴展更多postcss功能

    script: 處理js 引入 gulp-babel 并 @babel/preset-env 來處理 es6+ 功能

    page: 查看html文件為swig 文件渗勘,并且layouts,partials 文件夾下的為非入口文件 所以在src 添加ignore 排除這兩個目錄下的文件 在進行處理

    serve:拆分為 compile devServe 首先生成 temp內(nèi)容 然后開啟開發(fā)服務(wù)器 監(jiān)聽變化 顯示變化

    devServe:將生成在 temp 目錄下的文件 首先用watch 監(jiān)聽文件變化css js html 進行實時監(jiān)聽,實時編譯俩莽。 browserSync 開啟靜態(tài)服務(wù) 未查詢到的文件 可通過 路由處理 及 文件來源查找進行處理

    build:拆分為clean 及重新打包 (先 compileuseref )在打包靜態(tài)資源類 image旺坠,fontextra

    useref: 在temp下 根據(jù) html 注釋 解析文件 將資源文件css 和 js 打包 根據(jù)引用資源 在 壓縮js css html 生成在 dist 目錄下

    image扮超,font:經(jīng)過 imagemin 壓縮處理 生成在dist目錄下

    extra:直接拷貝到dist目錄下

    start:拆分為**build **及 prodServe 處理

    prodServe: 將生成的 dist 目錄下的文件 通過 browserSync 開啟靜態(tài)服務(wù)器

    deploy:拆分為 buildpublish 處理

    publish:將 生成的 dist 目錄下的文件 通過gulp-gh-pages 插件 進行處理 發(fā)布到 gh-pages分支下

    lint: 拆分為 stylesLintscriptLint 處理 又添加了 gulp lint --fix 添加修復(fù)功能

    styleLint: 通過gulp-postcss 結(jié)合相應(yīng) stylelint 插件 在根目錄下.stylelintrc.json 文件引入 stylelint-config-sass-guidelines 對sass 文件進行文件 lint

    scriptLint: 通過gulp-eslint 在根目錄下.eslintrc.js 引入 eslint-config-airbnb-base 進行 強校驗 保證代碼質(zhì)量

  • --production --port 5020 --open --fix 等 可通過 process.argv 獲取 查詢到 minimist 可進行相應(yīng)處理 處理結(jié)果 可以直接拿到

  • --production 判斷是否存在 存在可生成 js取刃,css sourcemaps 文件

  • 項目配置化,變量統(tǒng)一化出刷, 可讀取pages.config.js文件 可填寫相應(yīng)配置項 及 data數(shù)據(jù)

//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);

// 上線之前執(zhí)行的任務(wù)
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 完成項目的自動化構(gòu)建

說明:

? 查找 grunt 插件:https://www.gruntjs.net/plugins

  • 思路大體跟gulp一致 首先查找對應(yīng)功能的插件 根據(jù)插件文檔 編寫插件信息

  • 根據(jù)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 同步執(zhí)行

  • 處理 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']);
};


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市巷蚪,隨后出現(xiàn)的幾起案子病毡,更是在濱河造成了極大的恐慌,老刑警劉巖屁柏,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啦膜,死亡現(xiàn)場離奇詭異,居然都是意外死亡淌喻,警方通過查閱死者的電腦和手機僧家,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裸删,“玉大人八拱,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵掘殴,是天一觀的道長。 經(jīng)常有香客問我爹谭,道長,這世上最難降的妖魔是什么榛搔? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任诺凡,我火速辦了婚禮东揣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腹泌。我一直安慰自己嘶卧,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布凉袱。 她就那樣靜靜地躺著芥吟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪专甩。 梳的紋絲不亂的頭發(fā)上运沦,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音配深,去河邊找鬼。 笑死嫁盲,一個胖子當著我的面吹牛篓叶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羞秤,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼缸托,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘾蛋?” 一聲冷哼從身側(cè)響起俐镐,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哺哼,沒想到半個月后佩抹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡取董,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年棍苹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茵汰。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡枢里,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹂午,到底是詐尸還是另有隱情栏豺,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布豆胸,位于F島的核電站奥洼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏配乱。R本人自食惡果不足惜溉卓,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一皮迟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桑寨,春花似錦伏尼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沙咏,卻和暖如春辨图,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肢藐。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工故河, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吆豹。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓鱼的,卻偏偏與公主長得像,于是被迫代替她去往敵國和親痘煤。 傳聞我的和親對象是個殘疾皇子凑阶,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345