webpack生成html文件,用于后端渲染的研究

来源:互联网 发布:程序员的自我修养 ppt 编辑:程序博客网 时间:2024/06/05 05:30

不适用后端渲染的原因

webpack的打包方式是把所有的资源都打包成bundle.js,并用一个没有内容的html引入生成的bundle.js,不太熟悉的同学可以参看慕课网的视频教程。但是如果公司的建站方式是后端渲染的话(如jsp),那就不能使用webpack了,因为webpack会把html也打包在bundle.js中。本文就是介绍如何用webpack生成我们需要的html,以及其中的问题和优化。

主要思路

webpack的html-webpack-plugin插件,可以设置一个template,我们可以在这个template上做文章,配合上相应的loader,就可以生成我们需要的html。

项目结构

下面是我的webpack目录结构

这里写图片描述

HTML部分

如何生成我们需要的html文件呢?

html-webpack-plugin的使用

我们采用曲线救国的方式生成我们需要的html,用于后端渲染。这就要使用到html-webpack-plugin的template属性。

module.exports = {  entry: 'index.js',  output: {    path: __dirname + '/dist',    filename: 'index_bundle.js'  },  plugins: [    new HtmlWebpackPlugin(),    // 生成一个空的html,用于引入webpack打包好的js文件    new HtmlWebpackPlugin({     // 再生成一个html      filename: 'test.html',      template: 'src/assets/test.html'    //注意这里可以使用一个template作为要生成的html的模板    })  ]}

上面代码中的template这里是一个html,官方的介绍是,你可以使用jade或ejs等模板引擎,也就是说webpack不关心你使用什么作为模板,只要输出一串字符串就行,于是我大胆的使用一个js文件作为模板,输出一段字符串,结果完全可行。

这样就产生了很多种方案,比如用js文件作为模板,在这个js文件中require其他的html文件,进行字符串拼接,最后输出,但是这样的话,每个需要输出的页面都需要配置也个这样的js文件,懒惰的我不允许这种事情的发生…..

再比如,直接使用一个html作为模板,配合使用html-loader,这个loader有一种引入其他文件的语法支持:

<div> ${require('./components/gallery.html')} </div>

这样我们就能引入其他的文件模块…..

但是有的时候我们需要一些模板语法,例如我们需要循环生成li标签,最后我采用的方案是使用 ejs 作为模板文件,采用 underscore-template-loader 作为我的loader,而没有采用 ejs-loader ,因为 ejs-loader 不会处理文件html结构中的图片路径问题,而且 ejs-loader 也没有require其他ejs文件的语法支持,虽然 ejs-loader 官方推荐使用 ejs-compiled-loader 用它来引入其他的ejs模块,但是这样显得很麻烦,而且图片路径问题还是没有解决。然后我就找到了神器 underscore-template-loader ,首先图片路径问题loader会帮你解决,其次该loader支持两种require其他文件的语法:

<div class="top-section">    @require('header.ejs', {"title": "First Section"})</div>

这里引入一个ejs文件,并向其中传入对应的值。(如果你看不懂上面的代码,可以先熟悉一下ejs语法)这样就能很轻松的引入一个component文件,并传入值,此外,如果你只想引入一段不带语法的html结构(即纯字符串),也可以采用下面的写法

<div class="wiki">    <h3>Introduction</h3>    @include('intro.htm')    <h3>Authors</h3>    @include('authors.htm')</div>

include只会将文件转换成字符串,并引入,所以确保你要引入的文件没有被loader处理过,不然很可能引入的是一个函数,而不是一串html结构。我在layout目录下设置不同的文件夹,一个文件夹代表一个页面,其中的ejs文件作为html-webpack-plugin的template,用这个ejs文件去require其他的component,如下图:

这里写图片描述

到这里我,我们基本都已经解决了html结构的部分,那css应该写在哪里呢?另外我不想把css也打包在bundle.js中,我想要生成单独的css文件,怎么办?

生成CSS文件

extract-text-webpack-plugin的使用

下面我们介绍另外一个webpack的插件:extract-text-wepack-plugin,这个插件用于提取出css文件。
我把一个页面的css放在layout下(这里我用的是sass),用这个css去require其他的component的css,如下图:

这里写图片描述

这个总的css是有了,可是我们把它放在哪呢?任何资源只有被入口的js文件require,才能被webpack处理,所以我们当然是用layout下的入口js文件去require这个css,但是这样css也就会被打包到bundle.js中,于是我们可以在这里使用extract-text-wepack-plugin 怎么用呢?主要是两步:

首先在rule中,对所有的scss文件使用extract-text-wepack-plugin

rules: [{        test: /\.scss$/,        use: ExtractTextPlugin.extract({                 fallback: "style-loader",                 use: ["css-loader?importLoaders=2","postcss-loader","sass-loader"]             })}]

其次在plugin中使用extract-text-wepack-plugin

plugin: [new ExtractTextPlugin('[name]/[name].css')]

特别注意,我们还需要在webpack.config.js文件的头部引入extract-text-wepack-plugin 这个模块,不然ExtractTextPlugin 就会没有定义,从而报错:

const ExtractTextPlugin = require("extract-text-webpack-plugin");

打包一下,在dist文件下,就能看到我们需要的css文件了。并且生成好的html也自动引入了这个css文件。

如何热更新?

当我们开启webpack的webpack-dev-server后,发现改动html和css都不能产生热更新,只有改动js才能热更新,我们可以在github官方页面上找到答案,如下图:

这里写图片描述

也就是说 extract-text-webpack-plugin 不支持热更新,于是我们可以这样来改进它,使用两种环境,一种是开发环境,一种是生产环境,在开发环境中,我们用入口的js去 require layout文件下的ejs模板,而不再使用html-webpack-plugin生成。也就是说,在开发的时候,所有的资源都是打包在bundle.js文件中的,只有在生产的时候,才像上面我们说的那样生成html和css,大概思路就是这样,那我们可以写两份不同的webpack的config文件,一个是开发另一个用作生产。

但是我只用了一个webpack.config.js,我们可以使用node提供给我们的一个API,来设置一个全局的值,使用方法如下:

//package.json"scripts": {    "build": "set NODE_ENV=production&& webpack -p --color"}

使用set NODE_ENV=production 就可以设置这个全局的值了,这里设置的是production,注意: production&& 之间不能有空格,不然这个全局的值就设置成'production ' ,production后面多了一个空格,怎么获取这个值呢?我们可以在程序的任意位置,通过process.env.NODE_ENV 来拿到这个值,做一个 if 判断就可以知道是开发环境还是生产环境了。

以css为例,配置如下:

rules: [{   test: /\.scss$/,   use: process.env.NODE_ENV == 'production'        ? ExtractTextPlugin.extract({ fallback: "style-loader", use: [              "css-loader?importLoaders=2",              "postcss-loader",              "sass-loader"            ]           })        : ['style-loader','css-loader?importLoaders=2',"postcss-loader",'sass-loader']}]

这样就可以用一份配置实现不同的需求,当然,我们还可以使用webpack提供的一个插件:DefinePlugin 把这个值暴露给整个webpack

plugin: [new webpack.DefinePlugin({            'ENV': JSON.stringify(process.env.NODE_ENV)//获取到NODE_ENV的值,并暴露为全局变量        })]

这样我们在任意的地方都可以直接使用ENV 这个值了。

到这里就结束了,抛砖引玉说了一下大致的思路,没有展开谈具体细节,我把这个脚手架开源在github上,(https://github.com/cwj0130/webpack-cli),大家可以clone参看具体细节,我也在完善部分功能,例如单元测试等,如果此教程对您有帮助,麻烦在github上给个star,谢谢。

此文为原创,可以任意转载,但请标明出处。