webpack 之多页面打包以及开启服务

472 阅读4分钟

例如当我们有一个项目,有手机端也有pad端,我们又不想单独拆分开两个项目,这时候我们可以使用多页面打包避免因为拆分项目导致一些问题出现

项目结构
src
	-- css
	    a.css
	    b.css
	-- js
    	    a.js
    	    b.js
    -- html
    	    template.html
// a.css
body {
    background: 'red'
}
// b.css
body {
    backgroundL 'pink'
}
// a.js
import '../css/a.css'
// b.js
import '../css/b.js'
 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>

    <%= htmlWebpackPlugin.options.date %>
        我是模板

    <!--我是注释-->

</body>
</html>
// webpack.config.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');  // 每次打包将之前的打包文件删除
const htmlWebpackPlugin = require('html-webpack-plugin'); // 生成一个html文件,自动将js文件引入
const miniCssExtractPlugin = require('mini-css-extract-plugin'); // 将css文件单独打包到一个文件中,再通过link标签引入
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); // 优化css温嘉输出  摒弃重复样式 砍掉样式中多余的参数 移除不需要的浏览器前缀

module.exports = {
  mode: "development", // 开发模式
  entry:{  // 打包的入口的文件路径
    a:'./src/a.js',
    b:'./src/b.js'
  },
  output: {
    filename: "main.js", // 打包结束之后的文件夹名称
    path: path.resolve(__dirname, 'build')
  },
  optimization: {
    concatenateModules: true
  },
  module: {
    rules: [
      {
        test: /\.(jpg|png|gif)$/,
        use: [{
          loader: "url-loader",
          options: {
            limit: 8192 // 小于则打包成base64 大于按原文件输出
          }
        }]
      },
      {
        test: /\.(css|scss)$/,
        use: [
          'style-loader', // 将css通过style标签插入到head中
          {
            loader: "css-loader", // 解析多个css关系,最后合并成一个
            options: {
              importLoaders: 2 // 表示通过import 语法引进的css文件也需要走下面的loader处理
            }
          },
          'sass-loader', // 解析sass文件
          'postcss-loader'
        ]
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({   // 生成模板html文件
      title: "My App",  // html 文件的标题
      template: "src/html/template.html", // html 模板的文件
      filename: "index.html" // 打包之后html 的文件
    }),
    new CleanWebpackPlugin(),  // 清除上一次打包的文件
    new miniCssExtractPlugin({  // 将css单独打包 再通过link引入
      filename: 'css/base.css'
    }),
    new optimizeCssAssetsWebpackPlugin({ // 删除不必要的css 重复css
      assetNameRegExp: /\.css$/g,
      cssProcessorOptions: {
        safe: true,
        autoprefixer: {
          disable: true
        }
      },
      mergeLonghand: false,
      discardComments: {
        removeAll: true // 移除注释
      },
      canPrint: true
    }),
    new HtmlWebpackPlugin({
       filename:'a.html',  //打包后文件的名字
       template:'./src/template.html',//   模板文件的名字
       // inject:false,   //是否将打包的css js文件注入到html中
       // minify:{
       //     collapseWhitespace:true,    //压缩打包后的html代码
       // }
       inject:false,
       chunks:['a'],
       title:'我是a.html的标题',
       date:new Date()
     }),
     new HtmlWebpackPlugin({
       filename:'b.html',  //打包后文件的名字
       template:'./src/template.html',//   模板文件的名字
       // inject:false,   //是否将打包的css js文件注入到html中
       // minify:{
       //     collapseWhitespace:true,    //压缩打包后的html代码
       // },
       inject:false,
       chunks:['b'],
       title:'我是b.html的标题'
     })
  ]
}

其实就是在entry 中配置多个入口,在vue中就是在src中建立多个项目,例如移动端一个, 平板端一个,使得代码复用更高,开发效率最快

wepback 热更新服务

当前的webpack项目中,我们每改动一个文件就要重新手动打包,这时候就需要一个类似于实时更新打包的服务了,类似于 Vue(Vue 也是基于webpack的,后续会进行讲解)

首先

需要安装一个webpack-dev-server

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

package.json 文件配置
// 在script脚本命令中添加指令
"script": {
    "watch": "webpack --watch",
    "start": "webpack-dev-server"
}

webpack.config.js 配置

devServer: { // 指的是我们临时搭建一个服务器
    contentBase: './build', // 服务器的根目录位置
    open: true, // 运行npm run start 自动打开浏览器
    port: 8000, // 端口
    hot: true, // 是否热更新
    hotOnly: true   // 即便热更新,也不刷新浏览器
}
对上述部分配置进行的说明

为什么我们要使用临时搭建的一个服务器

  1. 模拟实际运行环境,查找错误
  2. 有些请求或者资源或者功能需要在服务器环境下才能运行 例如 cookie
  3. 可以呈现实时预览效果

为什么webpack-dev-server 打包时候看不见文件

​ 为了提升打包效率,这些文件都被放在内存中

hotOnly

​ 对于css文件来说,不刷新浏览器并不会影响什么,你甚至可以参照更改之前和更改之后的样式

​ 但是对于js文件来说,效果是累加的,就例如有一个按钮,在之前是生成一个红色的正方形,更改之后由于页面没有刷新,导致之前的效果还在,但是你在点击按钮的时候,你改了啥颜色就变成啥颜色,这时候你需要单独对每一个模块进行处理

// index.js
import {createHeader} from "./header";
import {createContent} from "./content";
import {createSidebar} from "./sidebar";
import '../css/overall.css'  // 引进css文件
import '../css/index.scss'

let root = document.querySelector('body');

createHeader(root);
createSidebar(root);
createContent(root)

if (module.hot) {
	module.hot.acceot('./content.js', () => {
        createContent()
    })    
}

通过对module.hot判断是否得知开启了热更新,上面的accept就是监听某个文件是否发生变化,如果变化了,那么就执行这个函数

有些框架的loader时帮我们实现这个公告,不需要手动去写 比如 vue-loader

扩展知识

其实我们的 webpack-dev-server 的服务是支持写一点mock数据或者模拟后端直接返回数据(node.js 语法)

devServer: { // 指的是我们自己临时搭建的一个服务器
    contentBase: './build', // 服务器的根目录位置
    open: true, //  运行npm run start 之后会自动默认打开浏览器
    port: 8000, // 定义端口
    hot: true, // 开启热更新
    hotOnly: true, // 即便HMR(热更新)不生效也不会刷新浏览器
    proxy: { // 代理
      '/api': {
        target: 'http://localhost:3000', // api开头的路径就转发
        pathRewrite: {
          '/api': '/' // 路径重写
        }
      },
      before(app) {
            app.get('/api/user',(req,res)=>{
                res.send({
                  code: 0,
                  data: []
                })
            })
        // ...
      }
    }