Create SRC directories. Create front-end directories and node directories

The general directory is divided into

├ ─ ─ the config │ ├ ─ ─ htmlAfterWebpackPlugin. Js │ ├ ─ ─ webpack. Development. The js │ └ ─ ─ webpack. Production. The js ├ ─ ─ gulpfile. Js ├ ─ ─ Package. The json ├ ─ ─ postcss. Config. Js ├ ─ ─ the SRC │ ├ ─ ─ nodeuii │ │ ├ ─ ─ app. Js │ │ ├ ─ ─ the config │ │ │ └ ─ ─ index. The js │ │ ├ ─ ─ Controllers │ │ │ ├ ─ ─ ControllerInit. Js │ │ │ └ ─ ─ IndexController, js │ │ ├ ─ ─ middlewares │ │ │ └ ─ ─ errHandler. Js │ │ ├ ─ ─ Models │ │ │ └ ─ ─ IndexModel. Js │ │ └ ─ ─ views │ │ └ ─ ─ index. The HTML │ └ ─ ─ webapp │ ├ ─ ─ views │ │ ├ ─ ─ common │ │ │ └ ─ ─ Layout. HTML │ │ └ ─ ─ index │ │ ├ ─ ─ the index - index. Entry. Js │ │ └ ─ ─ pages │ │ └ ─ ─ index. The HTML │ └ ─ ─ widgets │ └ ─ ─ the header │ ├ ─ ─ │ ├─ ├─ ├─ ├─ ├.org.txt │ ├─ ├.org.txt │ ├─ ├.org.txt │ ├─ ├.org.txtCopy the code

With the project body created, start creating the automated build process

Automated build process

Tool: gulp gulp-babel gulp-watch

1. npm init -y

2. Create gulpfile.js and write the automated process

const gulp = require('gulp');
const babel = require('gulp-babel');   // Automatically compile es6 to ES5
const watch = require('gulp-watch');   // gulp listens for file changes
const rollup = require('gulp-rollup'); // Do file cleaning, delete useless code
const replace = require ('rollup-plugin-replace'); // Replace files when they are cleaned
const gulpSequence = require('gulp-sequence')  // gulp task execution order<! --> gulp.task()'buildenv'.function () {
  return watch('./src/nodeuii/**/*.js', 
  { 
    ignoreInitial: false It needs to be executed before the first file modification, that is, immediately after watch() is called
  }, () => {
    gulp.src('./src/nodeuii/**/*.js')
      .pipe(babel({
        babelrc: false.// Ignore the. Babelrc file under the root directory
        "plugins": [["transform-decorators-legacy"."transform-es2015-modules-commonjs", {
            "allowTopLevelThis": true
          }]
        ]
    }))
    .pipe(gulp.dest('dist')); // Output to the dist folder})});<! Build task of production environment, compile ES6 to ES5gulp.task('buildprod', function () { gulp.src('./src/nodeuii/**/*.js') .pipe(babel({ ignore: ['./src/nodeuii/config/*.js'], babelrc: false, "plugins": [ ["transform-decorators-legacy", "transform-es2015-modules-commonjs", { "allowTopLevelThis": true }] ] })) .pipe(gulp.dest('dist')); }); // Start the cleaning stream, Tree-shaking gulp.task('buildconfig', () => { gulp.src('./src/nodeuii/**/*.js') .pipe(rollup({ output:{ format: 'CJS', / / output files to the format of the commonjs}, input: '. / SRC/nodeuii/config/index. Js', / / the specified file rollup plugins: [ replace({ 'process.env.NODE_ENV': Json.stringify ('production') // use rollup-plugin-replace to replace variables in files such as})]})).pipe(gulp.dest('./dist')); }); let _task = ['buildenv']; if (process.env.NODE_ENV == 'production') { _task = gulpSequence(['buildprod','buildconfig']); } gulp.task('default', _task);Copy the code

Tree-shaking

Gulp-rollup rollup-plugin-replace Gulp-sequence (gulp-rollup) : gulp-rollup (gulp-rollup) : gulp-rollup (gulp-rollup) : gulp-rollup

App.js file

const init = () => {
  if (process.env.NODE_ENV == 'development') {
    const developConfig = {
      port: 8081
    }
    config = _.extend(config, developConfig);
  }
  if (process.env.NODE_ENV == 'production') {
    const prodConfig = {
      port: 8081
    }
    config = _.extend(config, prodConfig);
  }
  return config;
}

export default init();
Copy the code

The environment is cleaned online

const init = () => {
  {
    const prodConfig = {
      port: 8081
    };
    config = _.extend(config, prodConfig);
  }
  return config;
};

var index = init();

module.exports = index;
Copy the code

A careful comparison of the two pieces of code reveals that, after cleaning, only the code needed for the production environment is left, simplifying the code;

3. Write the main entry file app.js

The node uses KOA2 + KOa-simple-router + KOa-swig + Log4JS

import Koa from 'koa' // koa 2.x
import router from 'koa-simple-router'
import render from 'koa-swig';
import log4js from "log4js";
import controllerInit from './controllers/ControllerInit';
import config from './config'
import errorHander from './middlewares/errHandler';

const app = new Koa();
<! -- Koa static file specifies middleware koa-static -->
const serve = require('koa-static');
<! The co module can operate asynchronously -->
const co = require('co'); 
<! View layer files -->app.context.render = co.wrap(render({ root: config.viewDir, autoescape: True, varControls:["[[","]]"], // Set Vue {{}} cache: 'memory', // disable, set to false ext: 'html', })); log4js.configure({ appenders: { cheese: { type: "file", filename: "./logs/rys.log" } }, categories: { default: { appenders: ["cheese"], level: "error" } } }); // Handling error center const logger = log4js.getLogger("cheese"); errorHander.error(app,logger); . / / all focus on routing controllerInit getAllRouters (app, the router); Use (serve(config.staticdir)) app.listen(config.port,() => {console.log(' app is listening on ${config.port}`) })Copy the code

Note:

Koa: EXPRESSIVE HTTP middleware framework for Node.js that makes Web applications and apis more enjoyable to write koA-static: KOA static files specify middleware koa-static co: Generator based NodeJS and browser control flow well, using Promises, allows you to write non-blocking code in a very good way koA-swig: Swig based KOA view rendering with support for tags, filters and extensions. Koa 2X is used with CO module when rendering pages using KOA-swiglog4js: log management cross-env: setting environment variablesCopy the code

Swig is a JS template engine

Use extends and block to implement template inheritance layout.html

  1. Layout.html is a public page body
<! DOCTYPE html> <html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock  %}</title>
    {% block head %}{% endblock  %}
</head>
<body>
    {% block content %}{% endblock  %}
</body>
</html>
Copy the code
  1. The index. HTML include template contains a template to the current location that will use the current context
{% extends '.. /common/layout. HTML '%} {% block title %} home {% endBlock %} {% block content %} {% include ".. /.. /widgets/header/header.html" %} {% endblock %}Copy the code

After node side testing is complete, start writing front-end automation builds

4. Write a webpack. Config. Js

const argv = require('yargs-parser')(process.argv.slice(2))
const merge = require('webpack-merge')
const glob = require('glob')
const files = glob.sync('./src/webapp/views/**/*.entry.js')

const _mode = argv.mode || 'development'
const _modeflag = _mode === 'production'
const _mergeConfig = require(`./config/webpack.${_mode}.js`)

const { join } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlAfterWebpackPlugin = require('./config/htmlAfterWebpackPlugin.js')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')

let _entry = {} // Webpack public entry
let _plugins = [] // WebPack public plug-in
for (let item of files) {
  //index-index.entry.js -> index.index.js
  if (/.+\/([a-zA-Z]+-[a-zA-Z]+)(\.entry\.js$)/g.test(item)) {
    const entrykey = RegExp. $1
    // console.log(entrykey);
    _entry[entrykey] = item
    //dist Outer folder name template inner HTML name
    const [dist, template] = entrykey.split(The '-')
    _plugins.push(
      new HtmlWebpackPlugin({
        filename: `.. /views/${dist}/pages/${template}.html`.template: `src/webapp/views/${dist}/pages/${template}.html`.minify: {
          collapseWhitespace: _modeflag,
          removeAttributeQuotes: _modeflag
        },
        inject: false}}}))let webpackConfig = {
  entry: _entry,
  output: {
    path: join(__dirname, './dist/assets'),
    publicPath: '/'.filename: 'scripts/[name].boudle.js'
  },
  module: {
    rules: [{test: /\.(sa|sc|c)ss$/.use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'postcss-loader'.'sass-loader']]}},watch: !_modeflag,
  watchOptions: {
    ignored: /node_modules/.aggregateTimeout: 300.poll: 1
  },
  optimization: {
    splitChunks: {
      chunks: 'async'.minSize: 30000.maxSize: 0.minChunks: 1.maxAsyncRequests: 6.maxInitialRequests: 4.automaticNameDelimiter: '~'.cacheGroups: {
        commons: {
          chunks: 'initial'.minChunks: 2.minSize: 0.name: 'conmons'}}},runtimeChunk: {
      name: 'runtime'}},plugins: [
    ..._plugins,
    new CleanWebpackPlugin({}),
    new MiniCssExtractPlugin({
      filename: 'styles/[name].[hash:5].css'
    }),
    new htmlAfterWebpackPlugin({ options: ' '}})]module.exports = merge(webpackConfig, _mergeConfig)
Copy the code

5. Write webpack.develop.js (development environment)

const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  plugins: [
    // Layout.html in views
    new CopyWebpackPlugin([
      {
        from: path.join(__dirname, '.. /src/webapp/views/common/layout.html'),
        to: '.. /views/common/layout.html'}]),new CopyWebpackPlugin(
      [
        {
          from: path.join(__dirname, '.. /src/webapp/widgets/'),
          to: '.. /widgets'}, {copyUnmodified: true.ignore: ['*.js'.'*.css']}),new MiniCssExtractPlugin({
      filename: 'styles/[name].css'}})]Copy the code

6. Write webpack.production.js (production environment)

const CopyWebpackPlugin = require('copy-webpack-plugin')
const minify = require('html-minifier').minify
const path = require('path')
module.exports = {
  output: {
    filename: 'scripts/[name].[hash:5].bundle.js'
  },
  plugins: [
    // Layout.html in views
    new CopyWebpackPlugin([
      {
        from: path.join(
          __dirname,
          '.. / ' + 'src/webapp/views/common/layout.html'
        ),
        to: '.. /views/common/layout.html',
        transform (content, path) {
          return minify(content.toString('utf-8'), {
            collapseWhitespace: true})}}]),new CopyWebpackPlugin(
      [
        {
          from: path.join(__dirname, '.. / ' + 'src/webapp/widgets/'),
          to: '.. /widgets',
          transform (content, path) {
            return minify(content.toString('utf-8'), {
              collapseWhitespace: true}}}], {copyUnmodified: true.ignore: ['*.js'.'*.css']})]}Copy the code

7. Write custom webpack – plugin = > htmlAfterWebpackPlugin. Js (to deal with documents after packaging)

const pluginName = 'htmlAfterWebpackPlugin'
const assetsHelp = data= > {
  let css = [],
    js = []
  const dir = {
    js: item= > `<script src="${item}"></script>`.css: item= > `<link rel="stylesheet" href="${item}"/ > `
  }
  for (let jsitem of data.js) {
    js.push(dir.js(jsitem))
  }
  for (let cssitem of data.css) {
    css.push(dir.css(cssitem))
  }
  return{ css, js } } <! -- check the documentation for htML-webpack-plugin v3, which contains hooks -->class htmlAfterWebpackPlugin {
  apply (compiler) {
    compiler.hooks.compilation.tap(pluginName, compilation => {
      compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
        pluginName,
        htmlPluginData => {
          let _html = htmlPluginData.html
          const result = assetsHelp(htmlPluginData.assets)
          console.log(result)
          // Use Cheerio vue SSR to insert different positions according to your template Settings
          _html = _html.replace('<! --injectcss-->', result.css.join(' '))
          _html = _html.replace('<! --injectjs-->', result.js.join(' '))
          htmlPluginData.html = _html
        }
      )
    })
  }
}
module.exports = htmlAfterWebpackPlugin
Copy the code

Package the project and run it

yarn client:dev
yarn start:dev
Copy the code

Visit: http://localhost:8081

Note:

Yargs-parser: Yargs helps you build the interactive command line tool package.json by parsing parameters and generating elegant user interfaces:"scripts": {
        "start:dev": "cross-env NODE_ENV=development supervisor ./dist/app.js"."build:dev": "gulp"."build:prod": "cross-env NODE_ENV=production gulp"."docs": "jsdoc ./src/nodeuii/**/*.js -d ./docs/jsdocs"."client:dev": "webpack --mode development"."client:prod": "webpack --mode production"
      },
    
webpack.config.js:
    var argv = require('yargs-parser')(process.argv.slice(2)); Console. log(argv.mode) // Development or production webpack-merge: Merge objects Many times, we need to do different operations for different environments. Webpack.product. js // Production environment required code webpack.dev.js // development environment required code glob: Pattern matching files used by the shell, such as asterisks. var glob = require("glob"); // options Optional glob("**/*.js", options, function(er, files) {// files is an array of filename. // If the option 'nonull' is set and no match is found, files is ["**/*.js"] // er is an error object or null. })Copy the code

For multiple files, the repeated file configuration is implemented to automatically get the file to be configured, and call in a loop; Eg: Use the glob module to get files

/ / into the glob
var glob = require('glob');
// Read all HTML files in the SRC directory
var files = glob.sync('./src/*.html');
var entry={};
var plugins=[];
// Loop the file
files.forEach(function(item,i){
    //item is similar to./ SRC /index.html
    var htmlName=item.slice(item.lastIndexOf("/") + 1);
    // The last filename to be generated only needs the last name index.html
    var name=htmlName.split(".") [0];
    // Add to the entry entry and specify the directory to generate the file
    entry["page/"+name+"/"+name]='./src/js/'+name+'.js'
    // Generate an htmlWebpackPlugin instance
    plugins.push(
        new htmlWebpackPlugin({
            template:item,
            filename:htmlName,
            chunks: ["page/"+name+"/"+name]
        })
    )
});

module.exports={
    entry:entry,
    output: {filename:"[name].[chunkhash].js".path:path.resolve(__dirname,'dist'),},module: {rules:[
          {
              test: /\.js$/.exclude: /node_modules/.use: {
                loader: 'babel-loader'}}},],plugins:plugins
}
// Use glob.sync to read files and loop through them. The loop takes care of the parts that need to be done manually.
Copy the code

Generate jsdocs

"docs": "jsdoc ./src/nodeuii/**/*.js -d ./docs/jsdocs"
Copy the code

Source address link:

Preliminary Discussion on front-end architecture

Welcome to communicate

Reference Documents:

  1. Lodash Chinese document; Lodash is a consistent, modular, high-performance JavaScript utility library.
  2. Jsdoc is a tool that generates API documents for javascript applications, libraries, and modules based on comments in javascript files
  3. Cross-env introduces scripts that run cross-platform Settings and use environment variables
  4. Rollup module packaging; Implement code cleaning
  5. Rollup Chinese document
  6. Co source code analysis
  7. Log4js is completely explained
  8. yargs-parser
  9. Webpack – the use of the merge
  10. The Node glob grammar
  11. copy-webpack-plugin
  12. HTML – webpack – plugin explanation
  13. extract-text-webpack-plugin
  14. HTML – minifier;