webpack多页应用配置

说明: 网上很多教程,大各种各样的内容都有,自己研究的时候查了很多文档,出现了一些错误都一一记录下来.并且配置文件没有任何的封装,旨在能提炼出最关键的内容.

环境

webpack: 4

1. 要用到的插件:

html-webpack-plugin // 用于根据html模板生成页面
clean-webpack-plugin // 用于清除dist目录下的代码
extract-text-webpack-plugin // 用于提取出css到单个或者多个单独文件中

2. 项目目录

 ProjectName
    ├ ─ dist  // 准备发布代码时build出来的文件在这里(需在webpack.config.js中配置)
    ├ ─ node_modules
    ├ ─ src
           ├ ─ css
                  ├ ─ index.css
                  ├ ─ about.css
           ├ ─ js
                  ├ ─ index.js
                  ├ ─ about.js
           ├ ─ pages
                  ├ ─ index.html
                  ├ ─ about.html
    ├ ─ favicon.ico // 可有可无,因为我项目里放了,就写在这里了
    ├ ─ package.json
    ├ ─ webpack.config.js // webpack 配置文件

3. 开始

1. 创建项目
mkdir mutile-page-demo 
cd mutil-page-demo
npm init -y
2. 创建目录
mkdir src
mkdir src/css
mkdir src/js
3. 创建文件(完全可以使用IDE创建出这个目录结构,不一定要使用命令行,我这里只是为了展示起来更直观)
# 注意使用git-bash或Cmder命令行工具,windows命令行中是没有 touch命令的
touch webpack.config.js 
touch ./src/pages/index.html
touch ./src/pages/about.html
touch ./src/css/about.css
touch ./src/css/about.css
touch ./src/js/about.js
touch ./src/js/about.js
4. 下面罗列文件内容,可以自己随便写,也可以copy下面的内容.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>index</title>
  <link rel="stylesheet" href="../css/index.css">
  <script src="../js/index.js">
  </script>
</head>
<body>
  <div class="title">this is index.html</div>
  <a href="about.html">about</a>
</body>
</html>

about.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>about</title>
  <link rel="stylesheet" href="../css/about.css">
</head>

<body>
  <div class="title">this is about.html</div>
  <a href="index.html">index</a>
</body>

</html>

index.js

window.onload = function () {
  var title = 'index'
  var p = document.createElement('p')
  p.innerText = 'append tag p from index.js!'
  document.body.appendChild(p)
}

about.js

window.onload = function () {
  var p = document.createElement('p')
  p.innerText = 'append tag p from about.js!'
  document.body.appendChild(p)
}

index.css

.title {
  color: red;
}

about.css

.title {
  color: blue;
}
5. 编写配置文件: webpack.config.js

module.exports 中有几个关键点:

  • entry: 入口文件: 简单理解为,webpack从这里开始编译,接受的参数类型
    • 字符串,如: entry: 'index.js' # 配置单页应用

    • 数组, 如: entry: ['index.js', 'about.js']

    • 对象(我使用这种方式): 如:

entry: {
    index: path.resolve(__dirname, 'src/js/index.js'),
    about: path.resolve(__dirname, 'src/js/about.js')
  },
  • output: 用于设置输出文件的配置(build)
  • module: 相关loader配置
  • plugins: 相关插件配置
  • devServer: 开发环境启动的服务,hot reload 在这里配置

直接贴部分代码并说明

entry

entry: {
    index: path.resolve(__dirname, 'src/js/index.js'), // 获得目标文件的绝对路径
    about: path.resolve(__dirname, 'src/js/about.js')
  },

output

path: path.resolve(__dirname, 'dist'), // 将build结果输出到 project 的 dist/ 目录下
filename: 'js/[name]-[hash].bundle.js' // 将js文件输出到 js/ 目录下 

module.rules 配置

module: {
    rules: [{
      test: /\.css$/,
      use: ['style-loader', 'css-loader'] 
    }]
  },

注意 style-loader要卸载css-loader的前面,否测会报错

image.png

plugins配置

new htmlWebpackPlugin({
  filename: 'index.html',  // 生成的html文件名称,可以同时指定输出的路径,如: /pages/index.html,将被输出到 /dist/pages/index.html
  template: path.resolve(__dirname, 'src/pages/index.html'), // 模板位置
  inject: 'head', // 控制webpack把js文件append到html的head还是body中,默认是body
  title: 'index', // 传递title到生成的html中
  chunks: ['index'], // 设置生成的html中要引入哪些js文件: chunk名称需要和entry中的属性保持一致,多个js以数组方式配置,如 chunks: ['index', 'about']
  // favicon: path.resolve('favicon.ico'), // 设置hmtl的shortcut icon
  // minify: { // 启用压缩功能
  //   removeComments: true,  //删除注释
  //   collapseWhitespace: true //删除空格
  // }
}),
new htmlWebpackPlugin({
  filename: 'about.html',
  template: path.resolve(__dirname, 'src/pages/index.html'),
  inject: 'head',
  title: 'about',
  chunks: ['about']
}),
......如果有多个页面需要也这样写

devServer

{
     // 注意设置了此属性将导致hot失效
     // 并且会导致js中通过import引入css出现一些问题
    // openPage: 'src/pages/index.html',

    open: true, // 自动打开浏览器并访问页面
    inline: true, // 内联模式实时刷新,推荐方式
    hot: false, // 热更新,注意当设置为true时,会导致热更新失败,浏览器不再刷新代码,原因不知
    port: 9527,
    host: 'localhost',
    // compress: true, // 启用压缩,会增加服务器端和客户端的负载
  }
6 添加运行代码

在package.json的 scripts 中添加2行代码,保存!

"start": "webpack-dev-server --config webpack.config.js" 
"build": "webpack --config webpack.config.js",

在命令行中执行: npm start

image.png

查看浏览器页面中,发现样式并没有生效
因为webpack中没有像entry那样的节点来单独配置css的输出,所以html中默认的link并不能让webpack帮我们把css文件一起打包

解决方法: 在index.js中import需要的css, webpack的思想是一切皆为模块,所以css也可以通过import引入.

修改index.js为:

import '../css/index.css' # 新增加的一行
window.onload = function () {
  var p = document.createElement('p')
  p.innerText = 'append tag p from index.js!'
  document.body.appendChild(p)
}

保存后查看浏览器发现样式生效了


image.png

然后我们在命令行中执行一下: npm run build
查看procjet中生成了dist目录:

image.png

发现我们预想的css目录没有出现,但是如果查看 由index.js生成的文件末尾(需要格式化一下代码,也可以直接搜索名称为title的样式),会看到 index.css中的代码
image.png

原来webpack把我们的css代码编译到了js中,在打开页面的时候,样式会被追加到html的style标签中.
为了解决这个问题,使用 extract-text-webpack-plugin 插件抽取css

7 使用 extract-text-webpack-plugin 插件抽取css

如果你还未安装此插件,需要安装

cnpm i -D extract-text-webpack-plugin

在webpack.config.js中,添加以下代码

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

将 modules.exports rules中 css的配置更改掉:

use: ExtractTextPlugin.extract({
 fallback: "style-loader",
 use: "css-loader"
})


image.png

改为


image.png

在plugins节点中,添加一行

 new ExtractTextPlugin({
    filename: 'css/[name].css'
 })

再次执行 npm run build,得到正确的输出

image.png

8. 如果你仔细看,会发现当你修改过js文件后,再执行 npm run build,会发现 /dist/js 中出现了多个带有不同hash串的 相同文件名. 比如: index-85eb01adecdeb439d2cb.bundle.js 和 index-fe758ed5a5b3da38a76e.bundle.js,见下图.
image.png

这是因为hash的原因,webpack在打包的时候会计算文件的hash值,如果文件被改动,那么整个编译出来的hash串也都会变化.(可以通过下面扩展中说明的chunkhash 和 contenthash来优化output节点中的 filename)
这时,最简单的方法就是build之前先清空掉dist,所以使用了 clean-webpack-plugin 插件

在 webpack.config.js 中加入一行

const {
  CleanWebpackPlugin
} = require('clean-webpack-plugin'); // 文件夹清除工具

并且在plugins节点中增加:

new CleanWebpackPlugin(), // 清空dist文件夹
image.png

扩展:

hash和chunkhash、contenthash区别
hash: hash是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值
chunkhash: chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响。
contenthash: 假设index.js引用了index.css,若使用chunkhash的话,只要js改变,那么css也会被重新构建。如何防止css被重新构建呢?
可以使用extract-text-webpack-plugin提取单独的css文件并使用contenthash,contenthash 只要文件内容不变,那么不会重复构建。

总结:

  1. 通过entry对象方式配置多入口
  2. 通过output将js输出到指定目录
  3. 通过html-webpack-plugin,指定输出多个index.html 页面
  4. 通过webpack-dev-server开启本地服务
  5. 通过import 'xxx.css' 配合 extract-text-webpack-plugin 插件修改,rules配置和在plugins中添加下面代码实现css抽离到单个文件或多个文件
new ExtractTextPlugin({ // 抽离到多个页面
    filename: 'css/[name].css'
  }) 

new ExtractTextPlugin("styles.css") // 抽离到单个页面

遇到的问题:

  1. 执行npm run build时报错
Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead

原因: extract-text-webpack-plugin还不能支持webpack4.0.0以上的版本。
解决方法: 安装最新版本

cnpm install --save-dev extract-text-webpack-plugin@next

发现的问题

  1. 如果plugins中配置了多个目录,并且有多个目录下同时拥有index.html,如果没有配置对应的index.html输出到指定的目录中,那么当npm start时自动打开的首页面是 new htmlWebpackPlugin()写在最后的一个页面,
    比如:
new htmlWebpackPlugin({
      template: path.resolve(__dirname, 'src/pages/zh-CN/index.html'),
      inject: 'head',
      title: 'index',
      chunks: ['index'],
      favicon: path.resolve('favicon.ico'),
      filename: 'index.html',
    }),
new htmlWebpackPlugin({
      template: path.resolve(__dirname, 'src/pages/zh-TW/index.html'),
      inject: 'head',
      title: 'index',
      chunks: ['index'],
      favicon: path.resolve('favicon.ico'),
      filename: 'index.html',
    }),
new htmlWebpackPlugin({
      template: path.resolve(__dirname, 'src/pages/test/index.html'),
      inject: 'head',
      title: 'index',
      chunks: ['index'],
      favicon: path.resolve('favicon.ico'),
      filename: 'index.html',
    }),

原因大概是,最后根据src/pages/test/index.html生成的index.html把前面的覆盖了.

  1. 当给devServer设置了 openPage: 'src/pages/zh-CN/index.html' 时,会导致hot失效,并且js中通过import引入css也会出现一些问题,并在浏览器中报错.

参考文档:

  1. https://blog.csdn.net/joyce_lcy/article/details/78627268#1-%E5%AE%89%E8%A3%85%E6%8F%92%E4%BB%B6
  2. https://segmentfault.com/a/1190000011434481?utm_source=tag-newest
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容