Webpack实战-04-03-构建单页应用

402 阅读4分钟

为单页应用生成HTML

引入问题

以上所有的栗子都是只输出一个bundle.js文件,所以手写了index.html文件取引入bundle.js文件,才能让应用再项目中运行起来,但是实际的项目比较复杂,一个页面会有很多资源要加载,我们会发现最终发布到线上的代码被内嵌了一些内容,部分文件名称打上根据文件内容算出的hash值,并且加载这些文件的url地址也被正常注入HTML中,所以手写index.html不现实,所以如何自动化盛昌符合要求的index.html文件

解决方案

推荐解决以上问题的插件:web-webpacj-plugin 1、修改Webpack配置文件如下

const path = require('path');
const UglifyJsPlugin = require('webpack/lib/optimize/UglifuPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const DefinePlugin = require('webpack/lob/DefinePlugin');
const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
    entry: {
        app: './main.js' // app的js执行入口文件
    },
    output: {
        filename: '[name]_[chunkhash:8].js', // 为输出的文件名称加上Hash值
        path: path.resolve(__dirname, './dist'),
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: ['babel-loader'],
                // 排除node_modules目录下文件
                // 该目录下的文件都采用了es5语法,没必要再使用babel转换
                exclude: path.resolve(__dirname, 'node_modules'),
            },
            {
                test: /\.css/, //增加对css文件的支持
                // 提取出Chunk中CSS代码到单独的文件中
                use: ExtractTextPlugin.extract({
                    use: ['css-loader?minimize'] //压缩css代码
                }),
            },
        ]
    },
    plugins: [
        // 使用本文的主角Webplugin,一个WebPlugin对应一个Html文件
        new WebPlugin({
            template: './template.html', // HTML模版文件所在的文件路径
            filename: 'index.html' // 输出的HTML文件的名称
        }),
        new ExtractTextPlugin({
            filename: `[name]_[contenthash:8].css`, // 为输出的css文件名加上一个hash值
        }),
        new DefinePlugin({
            // 定义NODE_ENV环境变量为production,以去除源码中只有开发时才需要的部分
            'process.env': {
                NODE_ENV: JSON.stringify('production')
            }
        }),
        // 压缩输出的js代码
        new UglifyJsPlugin({
            // 紧凑的输出
            beautify: false,
            // 删除所有注释
            comments: false,
            compress: {
                // 在UglifyJs删除没有用到的代码时不输出警告
                warnings: false,
                // 删除所有`console`语句,可以兼容IE浏览器
                drop_console: true,
                // 内嵌已定义但是只用到一次的变量
                collapse_vars: true,
                // 提取出出现多次但是没有定义成变量去引用的静态值
                reduce_vars: true,
            }
        })
    ]
}

其中template: './template.htm'所指的模版文件内容是

<html>
<head>
    <meta charset="UTF-8">
    <!--注入Chunk app中的css代码-->
    <link rel="stylesheet" href="app?_inline">
    <!--注入google_analytics中的js代码-->
    <script src="./google_analytics.js?_inline"></script>
    <!--异步加载XXX-->
    <script src="XXX" async></script>
</head>
<body>
<div id="app"></div>
<!--导入Chunk中的js代码-->
<script src="app"></script>
<!--XXX-->
<div id="xxx_thread"></div>
</body>
</html>

该文件描述了哪些文件以某种方式加入到输出的html中 <link rel="stylesheet" href="app?_inline">是按照正常引入css文件一样的语法引入webpack生产的代码,href属性中的app?_inline可以分为两部分,app表示css代码来自名叫app的chunk,_inline表示这个标签需要被嵌入到这个标签所在位置 <script src="./google_analytics.js?_inline"></script>表示js代码来自相对于当前模版文件template.html的本地文件./google_analytics.js,_inline表示这些代码要被内嵌到这个标签所在的位置

总结:资源链接url问号前面的内容表示资源来自哪里,后面的内容表示这些资源的注入方式

该插件出克支持_inline属性,还支持

  • _dist:只有在生产环境下才引入该资源
  • _dev:只有在开发环境下才引入该资源
  • _ie:只有在IE浏览器下才引入该资源

管理多个单页应用

引入问题

我们不能手动管理多个但页面应用的生成吧!!!不能吧,不能吧,当然不能,还能不能好好维护了 按照单页面应用的构建思路来说,我们如果手动维护 首先就要为每个单页应用添加配置

new WebPlugin({
    template: './template.html', // HTML模版文件所在的文件路径
    filename: 'login.html' // 输出的HTML文件的名称
}),

再改入口文件

entry: {
        index: './pages/index/index.js',
        login: './pages/login/index.js'
    },

好累啊,每加一个页面就要重新配置一遍,这不是我们咸鱼的性格!!!上解决方案

解决方案

使用AutoWebPlugin来解决以上问题

1、先看看目录要求

|-- pages 
        |-- index
            |-- index.js
            |-- index.css
        |-- login
            |-- index.js
            |-- index.css
    |-- common.css
    |-- google_analytics.js
    |-- template.html
    |-- webpack.config.js

从目录看出以下要求 1、所有单页应用都需要放到一个目录下,如pages 2、一个单页应用对应一个单独的文件夹 3、每个单页应用的目录下都有一个index.js文件作为入口执行文件

2、修改Webpack配置文件

const autoWebPlugin = new AutoWebPlugin('pages', {
    template:'./template.html', // HTML模版文件所在目录
    postEntrys: ['./common.css'], // 所有页面都依赖这份通用的css样式文件
    // 提取出所有页面的公共代码
    commonsChunk: {
        name: 'common', // 提取出公共代码Chunk的名称
    }
});
module.exports = {
    // AutoWebPlugin会为寻找到的所有单页应用生成对用的入口配置
    // autoWebPlugin.entry方法可以获取所有由AutoWebPlugin生成的入口配置
    entry: autoWebPlugin.entry({
        // 这里可以加入额外需要的Chunk入口
    }),
    plugins: [
        autoWebPlugin,
    ]
}

3、template.html文件大致如下

<html>
<head>
    <meta charset="UTF-8">
    <!--在这里注入该页面所依赖单没有手动导入的CSS-->
    <!--STYLE-->
    <!--注入google_analytics中的js代码-->
    <script src="./google_analytics.js?_inline"></script>
    <!--异步加载XXX-->
    <script src="XXX" async></script>
</head>
<body>
<div id="app"></div>
<!--导入Chunk中的js代码-->
<!--在这里注入该页面所依赖但没有手动导入的js-->
<!--SCRIPT-->
<!--XXX-->
<div id="xxx_thread"></div>
</body>
</html>

<!--SCRIPT--><!--STYLE-->是啥?因为Chunk不固定,不能写死,它保证了该页面所依赖的资源都会被注入生成的HTML模版里