webpack4搭建多页面应用以及代码复用

299 阅读3分钟

前言

在开发类似官网这类项目时,一般不会使用框架,1是因为框架体积大且官网这类项目业务逻辑比较少,基本上是静态页面,2 是不利于SEO,所以只能自己用webpack从头搭建,正好最近公司在做新的官网,因此在这里记录一下webpack4的一些配置和优化。

GitHub完整项目地址

1.多入口并自动生成html

entry: {
	index: "./src/js/index.js", // 首页
	about: "./src/js/about.js", //关于我们
},
plugins: [
  // 自动清空dist目录
  new CleanWebpackPlugin(),
  // 设置html模板生成路径
  new HtmlWebpackPlugin({
  	filename: 'index.html',
  	template: './src/views/index.html',
  	chunks: ['index']
  }),
  new HtmlWebpackPlugin({
  	filename: 'about.html',
  	template: './src/views/about.html',
  	chunks: ['about']
  }),
    ],
// 编译输出配置
output: {
	// js生成到dist/js,[name]表示保留原js文件名
	filename: 'js/[name.[hash].js',
	// 输出路径为dist
	path: path.resolve(__dirname, 'dist')
}

** 注意:在打包生产环境时,需在output添加publicPath属性,否则无法解析到资源目录 **

output:{
	...,
	publicPath: './'
}

当页面较多时,可以使用动态加载文件的方式来引入:

const fs = require("fs");
let htmlPlugins = [];
const files = fs.readdirSync(path.resolve(__dirname, "../src/views"));
htmlPlugins = files.map((item) => {
    return new HtmlWebpackPlugin({
        filename: item,
        template: `./src/views/${item}`,
        chunks: [item.split(".")[0]],
    });
});
module.exports = {
	......
  ...htmlPlugins,
}

entry也可以用同样的方式。

2. 热更新

npm install webpack-dev-server --save-dev

添加配置:

devServer: {
	contentBase: "./dist",
	port: 8888,
	hot: true,
},

3.编译es6

npm install babel-loader @babel/core @babel/preset-env --save-dev
npm install @babel/plugin-transform-runtime @babel/plugin-transform-modules-commonjs --save-dev
npm install @babel/runtime --save

配置如下:

{
  test: /\.js$/,
  exclude: /(node_modules|bower_components)/,
  use: {
  loader: "babel-loader",
  options: {
    presets: ["@babel/preset-env"],
    plugins: ["@babel/plugin-transform-runtime", "@babel/plugin-			transform-modules-commonjs"],
    },
  },
},

4.加载css和scss

安装相关依赖

npm install style-loader css-loader --save-dev
npm install sass-loader node-sass --save-dev

将CSS提取为独立的文件

npm install mini-css-extract-plugin --save-dev

添加配置:

rules: [
  {
  	test: /\.css$/,
  	use: [MiniCssExtractPlugin.loader, "css-loader"],
  },
  {
  	test: /\.scss$/,
  	use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
  }
]

5.打包css中的图片

安装url-loader

npm install url-loader --save-dev

增加配置:

rules: [
	...,
  {
    test: /\.(png|svg|jpg|gif|webp)$/,
    use: [
      {
        loader: "url-loader",
        options: {
          // 图片输出的实际路径(相对于dist)
          outputPath: "images",
          // 当小于某KB时转为base64
          limit: 0,
          name: "[name].[ext]",
          esModule: false, //高版本url-loader需添加这个属性
        },
      },
    ],
  },
]

6.加载html中的图片

安装依赖:

npm install html-loader --save-dev

添加配置:

rules:[
...,
{
  test: /\.(html)$/,
  use: [
    {
      loader: "html-loader",
      options: { // 改版后配置项发生了变化,请参考GitHub
        attributes: true,
        minimize: true,
      },
    },
  ],
},
]

7.处理第三方库,以jQuery为例

npm install jquery --save

引入后即可使用

import $ from 'jquery'

但如果项目中多个页面使用到,每个页面都引入会很麻烦,可以使用webpack的全局引入插件:

plugins: [
  ...,
  new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
  }),
]

之后在所有页面都能直接使用jquery而不用单独引入。

8.代码复用

开发多页面应用时经常会出现代码需要多处使用的情况,比如官网的头部和底部,而复制粘贴又太low,所以我们需要实现代码复用。 在这里使用到的是html-withimg-loader这个loader,这个loader可以代替html-loader解决html中的图片加载问题,而且支持代码复用。 安装依赖:

npm install html-withimg-loader --save-dev

添加配置:

module:{
	//删除html-loader相关配置
	rules:[
		{
			test: /\.(html)$/i,
			loader: "html-withimg-loader", //解决html中使用img标签路径找不到问题
		}
	]
}

复用语法如下:

<div>
    #include("./layout/top.html")
    <!--子页面将被引入,并且子页面中的img标签同样会进行处理-->
</div>

那么就可以在页面中进行代码复用 如:index.html

<div>
	#include("../component/header.html")
</div>

header.css和header.js需在使用到的页面的js文件中引入, 如:index.js

import "./header.js";
import "../css/header.scss";

需要注意的是,html-withimg-loader并不能打包音视频文件,因此html中的多媒体文件需要单独在对应js文件中引入: 如:index.js

import "../images/home/video1.mp4"; //处理视频文件

9.打包优化

9.1使用BundleAnalyzerPlugin插件打包结果可视化的插件

安装依赖:

npm install webpack-bundle-analyzer --save-dev

添加配置:

plugins:[
	...,
	new BundleAnalyzerPlugin({
  	analyzerPort: 8889,
  })
]

9.2拆分模块

因为多个页面都会用到如jquery这样的第三方包,那么每个文件都会把jquery打包进去,导致体积过大,因为可以使用splitChunks来拆分模块,只打包一次:

optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        name: "vendor",
        test: /[\\/]node_modules[\\/]/,
        chunks: "all",
        priority: 10, // 优先级
      },
    },
  },
},