# 工程化实战

这篇没有分段呢,后面再补

基于node bff实战项目

根目录创建 scripts 文件夹 scripts 下创建 client 文件夹 client 下根据需要创建 dev.sh prod.sh server.sh

dev.sh

#客户端的开发环境
webpack --mode development
1
2

在 config 文件夹下创建 webpack.development.js 和 webpack.production.js

在package.json 中使用 scripty 启动命令

{
    ...
    "client:dev": "scripty",
    "client:server": "scripty",
    "client:prod": "scripty"
    ...
}
1
2
3
4
5
6
7

安装scripty

yarn add scripty
1

安装完之后需要对scripts文件夹授权

chmod -R +x scripts/
1

创建 webpack.config.js 安装webpack

yarn add webpack
yarn add webpack-cli
1
2

调整目录结构 创建 src src 下创建 server 、web 将controllers 、models、middlewares、utils、logs、config都移到server目录下 web 下创建 componets、views views 下创建 books 、layouts books 下创建 pages pages 下创建 add.html index.html list.html layouts 下创建 layouts.html components 下创建banner、list 分别创建index.html、index.css、index.js

components/banner/index.html

<div class="banner">
    <ul>
        <li><a href="/list">图书列表页</a></li>
        <li><a href="/add">创建图书</a></li>
    </ul>
</div>
1
2
3
4
5
6

components/banner/index.js

const banner = {
    init() {
        console.log('banner js 入口');
    }
}
export default banner;
1
2
3
4
5
6

components/banner/index.css

.banner ul li {
    color:red;
}
1
2
3

components/list/index.html

<div class="list">
    <h1>图书列表页</h1>
</div>
1
2
3

components/list/index.js

const list = {
    init() {
        console.log('list js 入口');
    }
}
export default list;
1
2
3
4
5
6

components/list/index.css

.list h1{
    color:rebeccapurple;
}
1
2
3

layouts.html


<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>{% block title %}My Site{% endblock %}</title>

    {% block head %}

    {% endblock %}
</head>
<body>
    <div>
        {% block content %}{% endblock %}
    </div>
    {% block scripts %}{% endblock %}
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

分别将banner 和 list 的html引入 list.html

{% extends '../../layouts/layouts.html' %}

{% block title %}图书展示页面{% endblock %}

{% block content %}
{% include "@components/banner/index.html" %}
{% include "@components/list/index.html" %}
{% endblock %}
{% block scripts %}
<script>
console.log('图书展示页面')
</script>
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11
12
13

为了将banner 和 list 的js引入做准备,在pages下创建books-create.entry.js和books-list.entry.js books-list.entry.js

import banner from '@components/banner/index';
import list from '@components/list/index';
banner.init();
list.init();
1
2
3
4

全局安装 yargs-parser

yarn add yargs-parser
1

安装webpack-merge // 合并webpack.config

yarn add webpack-merge
1

安装 html-webpack-plugin // 将js插入到html中

yarn add html-webpack-plugin
1

安装 glob // 用来获取全局文件

yarn add glob
1

对 webpack.config.js 进行配置 将js分别插入到html中 webpack.config.js

// 拿到用户使用的参数 比如执行了 client:dev ,那就会拿到 mode:develoment
const argv = require('yargs-parser')(process.argv.slice(2));
// 拿到的参数
const _mode = argv.mode || 'development';
// 使用拿到的参数,获取对应环境webpack的配置文件
const _mergeConfig = require(`./config/webpack.${_mode}.js`);
// 用来合并webpack.config
const merge = require('webpack-merge');
// 用来将js插入到html中
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 找到全部文件
const glob = require('glob');
const { join } = require("path");

// 入口文件
let _entry = {};
// 插件
let _plugins = [new HtmlWebpackPlugin({
    filename: 'test.html',
    template: 'src/web/pages/list.html'
})];
// 寻找全部的entry
const files = glob.sync('./src/web/views/**/*.entry.js');
// 使用正则来匹配要找的js入口文件
for(let item of files) {
    if (/.+\/([a-zA-Z]+-[a-zA-Z]+)(\.entry\.js$)/g.test(item) == true) {
        // entryKey就是匹配到的文件名
        const entryKey = RegExp.$1;
        // 将拿到的文件名(books-list)拆分出来
        const [dist, template] = entryKey.split('-');
        // 将入口文件添加
        _entry[entryKey] = item;
        // 为不同的页面插入不同的js
        _plugins.push(new HtmlWebpackPlugin({
            // 路径是相对output里的path
            filename: `../views/${dist}/pages/${template}.html`,
            template: `src/web/views/${dist}/pages/${template}.html`
        }));
    }
}

const webpackConfig = {
    entry: _entry,
    output: {
        path: join(__dirname, "./dist/assets"),
        filename: 'scripts/[name].bundle.js'
    },
    // 添加别名,在views的html里使用了 @components 别名,来减少目录结构的查找
    resolve: {
        alias: {
            "@components": resolve(__dirname, 'src/web/components'),
        },
    },
    plugins: [
        ..._plugins
    ],
}
// 将合并后的配置导出
module.exports = merge(webpackConfig, _mergeConfig);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

此时运行一下

npm run client:dev
1

可以看到在dist目录下打包出了我们想要的 assets 和 views 但是在html中,将js插入在了最下方,这不是我们想要的,需要让js插入在script块内

{% extends '../../layouts/layouts.html' %}

{% block title %}图书展示页面{% endblock %}

{% block content %}
{% include "@components/banner/index.html" %}
{% include "@components/list/index.html" %}
{% endblock %}
{% block scripts %}
<script>
console.log('图书展示页面')
</script>
<!-- 应该将js插入在这里 -->
{% endblock %}
<script type="text/javascript" src="../../../assets/scripts/books-create.bundle.js"></script>
<script type="text/javascript" src="../../../assets/scripts/books-list.bundle.js"></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

同时 webpack 打包出来的文件中有很多重复的公共代码,很冗余,先用 runtime 进行一波优化 在webpack.config.js 中添加

...
const webpackConfig = {
    entry: _entry,
    output: {
        path: join(__dirname, "./dist/assets"),
        filename: 'scripts/[name].bundle.js'
    },
    resolve: {
        alias: {
            "@components": resolve(__dirname, 'src/web/components'),
        },
    },
    // 添加runtime进行抽离公共部分
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        }
    },
    plugins: [
        ..._plugins
    ],
}
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

运行打包后,会多出来一个 runtime.bundle.js 文件,将公共部分抽离出来

但是现在还有一个问题,插入的js文件不管三七二十一,把两个js文件都插入进来了,我们需要的是,list的html就插入list的js 继续修改 webpack.config.js文件, 在循环查找入口的_plugins中添加配置项

...
        _plugins.push(new HtmlWebpackPlugin({
            filename: `../views/${dist}/pages/${template}.html`,
            template: `src/web/views/${dist}/pages/${template}.html`,
            // 阻止自动插入js
            inject:false,
            // 手动指定插入,先插入 runtime,在将需要的js插入
            chunks: ["runtime", entryKey]
        }));
...
1
2
3
4
5
6
7
8
9
10

此时打包文件里没有插入js文件,接下来要搞点事情了,自己写一个webpack的插件 在config文件夹下创建 htmlAfterPlugin.js

const pluginName = 'htmlAfterPlugin';
const assetsHelp = (data) => {
    let js = [];
    const dir = {
        js: item => `<script src="${item}"></script>`
    }
    for(let jsitem of data.js) {
        js.push(dir.js(jsitem));
    }
    return {
        js
    }
}
class HtmlAfterPlugin {
    apply(compiler) {
        compiler.hooks.compilation.tap(pluginName, compilation => {
            // HtmlWebpackPlugin留下的钩子,在插入html之前等一下,必须要泡在HtmlWebpackPlugin插件之后
            compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(pluginName, htmlPluginData => {
                // htmlPluginData能拿到相应的html页面,替换掉写好的占位符
                // 获取对应的html
                let _html = htmlPluginData.html;
                _html = _html.replace(/@components/g, '../../../components');
                // 拿到要插入的js
                const result = assetsHelp(htmlPluginData.assets);
                // 将js文件替换占位符
                _html = _html.replace('<!-- injectjs -->', result.js.join(''));
                htmlPluginData.html = _html;
            })
        })
    }
}
module.exports = HtmlAfterPlugin;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

在webpack.config.js中引入

const HtmlAfterPlugin = require('./config/htmlAfterPlugin');
const webpackConfig = {
    entry: _entry,
    output: {
        path: join(__dirname, "./dist/assets"),
        filename: 'scripts/[name].bundle.js'
    },
    resolve: {
        alias: {
            "@components": resolve(__dirname, 'src/web/components'),
        },
    },
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        }
    },
    plugins: [
        ..._plugins,
        new HtmlAfterPlugin()
    ],
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

运行打包 npm run client:dev 可以看到html中的内容是我们想要的了,在script块中添加js,同时过滤掉无用的js

{% extends '../../layouts/layouts.html' %}

{% block title %}图书展示页面{% endblock %}

{% block content %}
{% include "../../../components/banner/index.html" %}
{% include "../../../components/list/index.html" %}
{% endblock %}
{% block scripts %}
<script src="../../../assets/scripts/runtime.bundle.js"></script>
<script src="../../../assets/scripts/books-list.bundle.js"></script>
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11
12

继续收拾一下,将 webpack.config.js 中的output的filename 去掉 在 config/webpack.development.js 中添加

module.exports = {
    output: {
        filename: 'scripts/[name].bundle.js',
        publicPath: '/'
    }
}
1
2
3
4
5
6

打包

{% extends '../../layouts/layouts.html' %}

{% block title %}图书展示页面{% endblock %}

{% block content %}
{% include "../../../components/banner/index.html" %}
{% include "../../../components/list/index.html" %}
{% endblock %}
{% block scripts %}
<script src="/scripts/runtime.bundle.js"></script>
<script src="/scripts/books-list.bundle.js"></script>
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11
12

现在还有layout.html等文件没有带过去,使用 copy-webpack-plugin

yarn add copy-webpack-plugin
1

webpack.development.js

const CopyPlugin = require('copy-webpack-plugin');
const {join} = require('path');
module.exports = {
    output: {
        filename: 'scripts/[name].bundle.js',
        publicPath: '/'
    },
    plugins: [
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/views/layouts/layout.html'), to: '../views/layouts/layout.html'}
        ]),
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/components'), to: '../components'}
        ], {
            ignore:['*.js','*.css'],
            copyUnmodified: true
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 使用gulp进行清洗

在package.json中添加指令

...
    "server:dev": "scripty",
    "server:prod": "scripty",
    "server:lint": "scripty"
...
1
2
3
4
5

在scripts 文件夹下创建 server 文件夹,创建dev.sh、lint.sh、prod.sh

dev.sh

gulp
1

lint.sh

cross-env NODE_ENV=lint gulp
1

prod.sh

cross-env NODE_ENV=production gulp
1

安装 cross-env

yarn add cross-env
1

安装gulp

yarn add gulp
1
yarn add gulp-watch
1

根目录创建gulpfile.js

const gulp = require('gulp');
const watch = require('gulp-watch');
const babel = require('gulp-babel');
const entry = '';

// 开发环境
function builddev() {

}

// 上线环境
function buildprod() {

}
function buildConfig() {

}
function buildLint() {

}

let build = gulp.series(builddev);

if (process.env.NODE_ENV == 'production') {
    build = gulp.series(buildProd, buildConfig);
}
if (process.env.NODE_ENV == 'lint') {
    build = gulp.series(buildLint);
}
gulp.task('default', build);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 将require 编译成 import

先将server下的所有文件的 require 都改成 import的方式

安装 gulp-babel

yarn add gulp-babel@7
1

修改gulpfile.js

const gulp = require('gulp');
const watch = require('gulp-watch');
const babel = require('gulp-babel');
//--------------------------------------------------------------------
const entry = './src/server/**/*.js';
//--------------------------------------------------------------------
// 开发环境
function builddev() {
    //--------------------------------------------------------------------
    return watch(entry, {ignoreInitial: false}, function() {
        gulp.src(entry).pipe(babel({
            // 不读取外面的babelrc配置,不需要编译成es5
            'babelrc': false,
            'plugins': ["@babel/plugin-transform-modules-commonjs"]
        }))
        .pipe(gulp.dest('dist'));
    })
    //--------------------------------------------------------------------
}

// 上线环境
function buildprod() {

}
function buildConfig() {

}
function buildLint() {

}

let build = gulp.series(builddev);

if (process.env.NODE_ENV == 'production') {
    build = gulp.series(buildProd, buildConfig);
}
if (process.env.NODE_ENV == 'lint') {
    build = gulp.series(buildLint);
}
gulp.task('default', build);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

安装 @babel/plugin-transform-modules-commonjs

yarn add @babel/plugin-transform-modules-commonjs
1

安装 babel-core

yarn add babel-core
1

执行

npm run server:dev
1

看dist 文件下的文件,将server的文件里用的import都转成了require,这样我们在写的时候就可以使用import去书写了 修改package.json 里的scripts 把 dev -> server:start

"server:start": "cross-env NODE_ENV=development node ./dist/app.js",
1

这个时候之前用的别名有个坑 要把package.json中的 _moduleAliases 改一下 package.json

...
"_moduleAliases": {
    "@root": ".",
    "@config": "./dist/config",
    "@models": "./dist/models"
  },
...
1
2
3
4
5
6
7

然后想要正常启动还有一个坑!!! 在 server/app.js 中将require改成 import 后,还要把 config 和 errorHandler解构处理

import 'module-alias/register';
import Koa from 'koa';
import controllersInit from './controllers/index';
import render from 'koa-swig';
import { wrap } from 'co';
import serve from 'koa-static';
//------------------------这俩货-----------------------------------
import config from '@config';
import errorHandler from "./middlewares/errorHandler";
//-----------------------------------------------------------
import { configure, getLogger } from 'log4js';
//------------------------这俩货-----------------------------------
const { error } = errorHandler;
const { viewDir, staticDir, port } = config;
//-----------------------------------------------------------
const app = new Koa();
configure({
    appenders: { cheese: { type: 'file', filename: __dirname + '/logs/yd.log' } },
    categories: { default: { appenders: ['cheese'], level: 'error' } }
});
const logger = getLogger('cheese');
app.context.logger = logger;
app.context.render = wrap(render({
    root: viewDir,
    autoescape: true,
    cache: false,
    ext: 'html',
    writeBody: false
}))
app.use(serve(staticDir));
error(app);
// 路由的注册中心
controllersInit(app);
app.listen(port, () => {
    console.log('服务启动成功!!');
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

再运行 npm run server:start 就可以看到服务启动成功了 打开 localhost:3001 此时报错,先把 src/server/controllers/IndexController.js 修改一下

import Controller from './BaseController';

class IndexController extends Controller{
    constructor() {
        super()
    }
    async actionIndex(ctx, next) {
        // ctx.body = await ctx.render('Index/index')
        ctx.body = {
            home: '首页数据'
        }
    }
}
export default IndexController;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

重启服务,显示'首页数据'

继续换其他路由试试,顺手把 src/server/controllers/BooksController.js 里的路由改一下

import Books from '../models/Books';

class BookController {
    async actionIndex(ctx, next) {
        const $model = new Books();
        const result = await $model.getList();
        console.log("返回的值", result);
        // ctx.body = result;
        ctx.body = await ctx.render('books/pages/list', {
            result
        });
    }
    async actionCreate() {
        ctx.body = await ctx.render('books/pages/create');
    }
}

export default BookController;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

localhost:3001/books/list

但是会报错,需要把 src/server/utils/SafeRequest.js 的import 引入改成解构

// 需要解构一下config
import config from '../config';
import { get as _get } from "axios";
// 需要解构一下config
const { baseUrl } = config;
class SafeRequest {
    constructor(url) {
        this.url = url;
        this.baseUrl = baseUrl;
    }
    get(params = {}) {
        let result = {
            code: 0,
            message: "",
            data: []
        }
        return new Promise((resolve, reject) => {
            _get(this.baseUrl + this.url, {
                params
            })
                .then(function (response) {
                    if (response.status == 200) {
                        const data = response.data;
                        result.data = data;
                        resolve(result);
                    } else {
                        result.code = 1;
                        result.message = "后台请求出错";
                        reject(result);
                    }
                })
                .catch(function (error) {
                    result.code = 1;
                    result.message = error;
                    reject(result);
                });
        })
    }
}
export default SafeRequest;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

然后又报了另一个错接着干,找不到layouts了,说明路径有问题,那么一步到位,直接把layouts也加入到webpack中

修改 src/web/views/books/pages/list.html 中的layouts路径,直接用 @layouts代替,同理把 create.html也改掉

{% extends '@layouts/layout.html' %}

{% block title %}图书展示页面{% endblock %}

{% block content %}
{% include "@components/banner/index.html" %}
{% include "@components/list/index.html" %}
{% endblock %}
{% block scripts %}
<!-- injectjs -->
{% endblock %}
1
2
3
4
5
6
7
8
9
10
11

再把 config/htmlAfterPlugin.js 修改

const pluginName = 'htmlAfterPlugin';
const assetsHelp = (data) => {
    let js = [];
    const dir = {
        js: item => `<script src="${item}"></script>`
    }
    for(let jsitem of data.js) {
        js.push(dir.js(jsitem));
    }
    return {
        js
    }
}
class HtmlAfterPlugin {
    apply(compiler) {
        compiler.hooks.compilation.tap(pluginName, compilation => {
            // HtmlWebpackPlugin留下的钩子,在插入html之前等一下,必须要泡在HtmlWebpackPlugin插件之后
            compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(pluginName, htmlPluginData => {
                // htmlPluginData能拿到相应的html页面,替换掉写好的占位符
                // 获取对应的html
                let _html = htmlPluginData.html;
                // 加入@layouts-----------------------------------------------------------
                _html = _html.replace(/@layouts/g, '../../layouts');
                _html = _html.replace(/@components/g, '../../../components');
                // 拿到要插入的js
                const result = assetsHelp(htmlPluginData.assets);
                // 将js文件替换占位符
                _html = _html.replace('<!-- injectjs -->', result.js.join(''));
                htmlPluginData.html = _html;
            })
        })
    }
}
module.exports = HtmlAfterPlugin;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

再稍稍改改之前遗留的bug src/server/controllers/BooksControllers.js actionCreate 忘了传 ctx 了

import Books from '../models/Books';

class BookController {
    async actionIndex(ctx, next) {
        const $model = new Books();
        const result = await $model.getList();
        console.log("返回的值", result);
        // ctx.body = result;
        ctx.body = await ctx.render('books/pages/list', {
            result
        });
    }
    async actionCreate(ctx, next) {
        ctx.body = await ctx.render('books/pages/create');
    }
}

export default BookController;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

接下来要开始用gulp对没有用的代码进行清洗 在dist 上线目录中我们看到,像config中还有if进行环境判断,这就很不友好,所以就要使用gulp进行清洗

安装 gulp-rollup

yarn add gulp-rollup
1

修改一下 gulpfile.js

const gulp = require('gulp');
const watch = require('gulp-watch');
const babel = require('gulp-babel');
const entry = './src/server/**/*.js';
//------------------------------新增------------------------------
const configEntry = './src/server/config/index.js';
const rollup = require('gulp-rollup');
//------------------------------新增------------------------------

// 开发环境
function builddev() {
    return watch(entry, {ignoreInitial: false}, function() {
        gulp.src(entry).pipe(babel({
            // 不读取外面的babelrc配置,不需要编译成es5
            'babelrc': false,
            'plugins': ["@babel/plugin-transform-modules-commonjs"]
        }))
        .pipe(gulp.dest('dist'));
    })
}

// 上线环境
function buildProd() {
    //------------------------------新增------------------------------
    return gulp.src(entry).pipe(babel({
        // 不读取外面的babelrc配置,不需要编译成es5
        'babelrc': false,
        'ignore':[configEntry],
        'plugins': ["@babel/plugin-transform-modules-commonjs"]
    }))
    .pipe(gulp.dest('dist'));
    //------------------------------新增------------------------------
}
function buildConfig() {
    //------------------------------新增------------------------------
    return gulp.src(entry)
    .pipe(rollup({
        input: configEntry,
        output: {
            format: 'cjs'
        }
    }))
    .pipe(gulp.dest('./dist'))
    //------------------------------新增------------------------------
}
function buildLint() {

}

let build = gulp.series(builddev);

if (process.env.NODE_ENV == 'production') {
    build = gulp.series(buildProd, buildConfig);
}
if (process.env.NODE_ENV == 'lint') {
    build = gulp.series(buildLint);
}
gulp.task('default', build);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

现在执行 npm run server:prod 看config里面,发现if分支还在,但是如果写其他的if的话就会被洗掉,所以还需要一些小插件来改变if条件

安装 rollup-plugin-replace

yarn add rollup-plugin-replace
1

在gulpfile.js中添加插件

const gulp = require('gulp');
const watch = require('gulp-watch');
const babel = require('gulp-babel');
const entry = './src/server/**/*.js';
const configEntry = './src/server/config/index.js';
const rollup = require('gulp-rollup');
//--------------------------------------------------------------
const replace = require('rollup-plugin-replace');
//--------------------------------------------------------------
// 开发环境
function builddev() {
    return watch(entry, {ignoreInitial: false}, function() {
        gulp.src(entry).pipe(babel({
            // 不读取外面的babelrc配置,不需要编译成es5
            'babelrc': false,
            'plugins': ["@babel/plugin-transform-modules-commonjs"]
        }))
        .pipe(gulp.dest('dist'));
    })
}

// 上线环境
function buildProd() {
    return gulp.src(entry).pipe(babel({
        // 不读取外面的babelrc配置,不需要编译成es5
        'babelrc': false,
        'ignore':[configEntry],
        'plugins': ["@babel/plugin-transform-modules-commonjs"]
    }))
    .pipe(gulp.dest('dist'));
}
//--------------------------------------------------------------
function buildConfig() {
    return gulp.src(entry)
    .pipe(rollup({
        input: configEntry,
        output: {
            format: 'cjs'
        },
        plugins: [
            replace({
                'process.env.NODE_ENV': JSON.stringify('production')
            })
        ]
    }))
    .pipe(gulp.dest('./dist'))
}
//--------------------------------------------------------------
function buildLint() {

}

let build = gulp.series(builddev);

if (process.env.NODE_ENV == 'production') {
    build = gulp.series(buildProd, buildConfig);
}
if (process.env.NODE_ENV == 'lint') {
    build = gulp.series(buildLint);
}
gulp.task('default', build);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

此时再执行 npm run server:prod 看dist中的config/index.js文件,再无if分支,干净的很

'use strict';

var lodash = require('lodash');
var path = require('path');

let config = {
    viewDir: path.join(__dirname,'..','views'),
    staticDir: path.join(__dirname,'..','assets')
};
{
    const prodConfig = {
        port: 80
    };
    config = lodash.extend(config, prodConfig);
}
var config$1 = config;

module.exports = config$1;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

eslint 省略

补全新增页面 src/web/components/add 修改views/books/pages/create.hmtl 、books-create.entry.js

webpack.config.js添加watch

...
const _modeFlag = _mode == 'development' ? true : false;
...
const webpackConfig = {
    ...
    watch: _modeFlag,
    ...
}
...
1
2
3
4
5
6
7
8
9

至此,服务端的上线环境打包完成

接下来继续搞客户端上线 在config/webpack.production.js中添加

先安装 html-minifier 来进行html的压缩美化

yarn add html-minifier
1

scripts/client/prod.sh

#开发上线环境
webpack --mode production
1
2

config/webpack.production.js

const CopyPlugin = require('copy-webpack-plugin');
const {join} = require('path');
const minify = require('html-minifier').minify;
module.exports = {
    output: {
        filename: 'scripts/[name].bundle.js',
        publicPath: '/'
    },
    plugins: [
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/views/layouts/layout.html'), to: '../views/layouts/layout.html'}
        ]),
        new CopyPlugin([
            {
                from: join(__dirname, '../', 'src/web/components'), 
                to: '../components',
                transform(content) {
                    const result = minify(content.toString('utf-8'), {
                        collapseWhitespace: true
                    });
                    return result;
                }
            }
        ], {
            ignore:['*.js','*.css']
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

执行 npm run client:prod 看dist中的component 里的html 都被压缩打包了

但是呢还是太丑了,所以再使用两个小插件 构建完成系统弹窗 安装webpack-build-notifier

yarn add webpack-build-notifier
1

在 config/webpack.development.js中添加插件

const CopyPlugin = require('copy-webpack-plugin');
const {join, resolve} = require('path');
//-----------------------------------------------------------------------------
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
//-----------------------------------------------------------------------------
module.exports = {
    output: {
        filename: 'scripts/[name].bundle.js',
        publicPath: '/'
    },
    plugins: [
        //-----------------------------------------------------------------------------
        new WebpackBuildNotifierPlugin({
            title: '我的SSR项目构建',
            logo: resolve('./logo.png'),
            supperssSuccess: true
        }),
        //-----------------------------------------------------------------------------
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/views/layouts/layout.html'), to: '../views/layouts/layout.html'}
        ]),
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/components'), to: '../components'}
        ], {
            ignore:['*.js','*.css'],
            copyUnmodified: true
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

运行 npm run client:dev 成功后会出现系统弹窗(我在虚拟机中做的啥也看不见- -)

安装 friendly-errors-webpack-plugin

yarn add friendly-errors-webpack-plugin
1

再添加插件

const CopyPlugin = require('copy-webpack-plugin');
const {join, resolve} = require('path');
const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
 //-------------------------------------------------------------------
const FriendlyErrorsPlugin  = require('friendly-errors-webpack-plugin');
 //-------------------------------------------------------------------
module.exports = {
    output: {
        filename: 'scripts/[name].bundle.js',
        publicPath: '/'
    },
    plugins: [
        //-------------------------------------------------------------------
        new FriendlyErrorsPlugin({
            compilationSuccessInfo: {
                messages: ['you application is running here http://localhost:3000'],
                notes: ['请及时注意后端与前端的编译入口']
            },
        }),
         //-------------------------------------------------------------------
        new WebpackBuildNotifierPlugin({
            title: '我的SSR项目构建',
            logo: resolve('./logo.png'),
            supperssSuccess: true
        }),
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/views/layouts/layout.html'), to: '../views/layouts/layout.html'}
        ]),
        new CopyPlugin([
            {from: join(__dirname, '../', 'src/web/components'), to: '../components'}
        ], {
            ignore:['*.js','*.css'],
            copyUnmodified: true
        })
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

安装这个插件本意是要消除掉乱七八糟的打包信息的,但是好像没太好用 - 0 -