《webpack初体验》

162 阅读6分钟

webpack是干嘛的?

  • 转译代码(ES6转为ES5,SCSS转为CSS)
  • 构建build
  • 代码压缩
  • 代码分析

打印出一个包的版本信息

npm info webpack

1. 用webpack转译JS

安装webpack可以本地安装,按照官网上的步骤:

在src目录里新建一个index.js文件,在终端:

npm init -y  //生成package.json
yarn add webpack webpack-cli --dev   //本地安装

出现node_modules目录,里边的.bin/webpack是可执行的文件

运行webpack:

npx webpack

npx可以自动找到webpack的路径,执行这句命令后,会出现一个dist目录,里边有一个main.js文件。

由于我们没有全局安装,只是把webpack本地安装在node_modules里。可以手动找: ./node_modules/.bin/webpack

webpack会智能的把JS代码转译成IE或低级浏览器能看懂的代码。变成能在所有浏览器运行的代码。

webpack.config.js

但是运行后发现有警告

configuration是配置的意思,是说mode选项没有被建立,把 mode设置成'development'或'production'模式。

按照官网的教程:

新建一个webpack.config.js文件,粘贴代码,但是注意:警告只说要设置mode,所以只写mode那句就可以

var path = require("path");

module.exports = {
  mode: "development",
};

npx webpack ,就没有警告了。

那'development'或'production'模式有什么区别呢?

'development'是给开发者看的,开发过程中可以设。代码里有很多注释。'production'是给用户看的,要开发完了可以设成这个模式。两个模式怎么切换后边会讲。

配置entry 和 output

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main.js",
  },
};

运行npx webpack。entry是入口文件,output是出口文件。如果不写,默认把src里的index.js转译成dist目录里的main.js。也可以改成其他文件名。

找找其他的配置:搜索 webpack filename hash,点击 caching(缓存),看到把filename改成了:

我们也把output的文件名改成画线那句,运行后,发现转译成了dist/main.e35684de36f86db66aa4.js文件

2. 理解文件名中hash的用途

HTTP缓存

第一次访问baidu.com,会首先加载index.html,再去服务器下载,引入其他css,js文件。关闭页面再次访问时,如果全部都要重新下载,性能太低。所以在http的响应头里加缓存控制:cache-control: public, max-age=31536000 表示任何人都可以缓存,保留时间是一年,一年之内再次来访问,从内存或硬盘里取就可以,不需要再去服务器下载。

如果其中的css文件要更新怎么办?缓存是根据文件名变化的,而webpack会自动一一对应。也就是说,如果我更改了文件的内容,再次运行,webpack会自动生成另一个文件,文件名和内容是哈希对应的。当再次加载页面时,index.html看到引入的文件名变了,就会马上去服务器下载新的文件,放在内存里。

index.html是不会缓存的,每次都是要重新下载的。

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[contenthash].js",
  },
};

所以webpack这个功能,就是根据内容产生一个哈希,只要内容变了,就会产生一个新的文件。

3. yarn build

因为每次webpack后都会生成一个新的文件,所以在npx webpack之前要先删掉dist rm -rf dist 把这两句话用一个build代替:

//package.json
"scripts": {
    "build": "rm -rf dist && webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

之后要运行,就只yarn build就可以

4. webpack生成HTML

官网步骤

安装

yarn add html-webpack-plugin --dev

配置

//webpack.config.js
var HtmlWebpackPlugin = require("html-webpack-plugin");  //新加的
var path = require("path");

module.exports = {
  //...//
  plugins: [new HtmlWebpackPlugin()],  //新加的
};

此时yarn build ,会在dist里默认生成一个index.html文件,是空的,自动引入js。 而且,如果我修改了js内容,html里引入的js文件名也会自动更新。

但是这个html的body里只有一个script,是空的。由于dist目录里全是新生成的,所以也不能直接在这里边改。怎么让html有其他标签呢:

new HtmlWebpackPlugin({
      title: "demo",
      template: "src/assets/index.html",
}),

如果只配置了title,在dist/index.html里title标签就变了。

template是模板,表示以"src/assets/index.html"这个文件为模板生成html。同时也要在src里创建目录,文件。当assets/index.html是个空文件时,可以看到dist/index.html里只有一句script,说明确实是以它为模板生成的。

把assets/index.html写入内容,作为模板:

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

title标签里的<>表示:生成的dist/index.html的title以配置文件里指定的title选项为准。

yarn build后,dist/index.html内容:

模板里啥样,生成的就啥样。只是会自动引入js

5. webpack引入css

官网步骤

先新建src/x.css,在x.js里引入css。

安装

yarn add css-loader --dev
yarn add style-loader --dev

配置

//webpack.config.js
module.exports = {
  module: {    
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
};

运行

yarn build

怎么预览页面看样式是否生效?

cd dist  //预览dist里的html,所以先进入到dist目录
hs . -c-1

可以看到样式生效。原理?

在配置文件里,意思是:找到以.css结尾的文件,'css-loader'负责把css内容引入到js文件中,此时还未生效。需要'style-loader'把js里的css内容加一个style标签,放到head里,样式就生效了。两个loader负责不同的部分。

6. webpack dev server

在引入css时,如果要改代码,需要不断进入dist目录预览页面和回到上一级目录build之间切换,太麻烦了。

webpack dev server可以简化这个过程,加快开发。不需要build

官网步骤

1. 配置

//webpack.config.js  添加:
 module.exports = {
    mode: 'development',
    entry:"",
    devtool: 'inline-source-map',
}

2. 安装

yarn add webpack-dev-server --dev
//webpack.config.js 里在devtool下边添加:
devServer: {
    contentBase: "./dist",
},

3. 添加脚本

//package.json
"scripts": {
    "start": "webpack-dev-server --open",  //添加
    "build": "rm -rf dist && webpack",
},

--open:自动打开浏览器,也可以去掉,去掉之后要手动搜索:localhost:8080

4. 运行

yarn start

在终端运行这句命令后,就会开始开发。打开浏览器。

这样就不需要再使用http server和yarn build。如果改变代码,保存之后,页面会自动更新,不需要别的命令了。

并不会生成dist目录,直接在内存里搞定,不依赖dist

7. css抽成文件

上边是使用JS生成style标签,另一种方法:用CssExtractPlugin这个插件

还是要在js里import css文件

安装

yarn add mini-css-extract-plugin --dev

配置

//webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");  //添加

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: "demo",
      template: "src/assets/index.html",
    }),
    new MiniCssExtractPlugin({   //添加
      filename: "[name].[contenthash].css",
      chunkFilename: "[id].[contenthash].css",
    }),
  ],
  module: {
    rules: [   //添加
      {
        test: /\.css$/i,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: "../",
              hmr: process.env.NODE_ENV === "development",
            },
          },
          "css-loader",
        ],
        //use: ["style-loader", "css-loader"],
      },
    ],
  },
};

要把上一种方法的use删掉,因为只使用一个loader。

插件里的filename也要加.[contenthash],为了让生成的css文件名也是哈希的,可以随着内容更新,用于缓存。

运行

yarn build

在dist里生成了一个css文件,并且html里也自动引入了这个css文件

预览

yarn start

8. 两种模式切换

  • 开发:用第一种模式,即JS生成style。使用yarn start
  • 生产:用第二种模式,即抽成css文件。使用yarn build

因为生成style标签很快,节省开发时间。而生产是最终给用户看的,需要缓存和生成css文件。

使用两个webpack.config.js文件

原来的默认文件用作开发,再复制一份新的叫webpack.config.prod.js用作生产。

webpack.config.js

//还是使用原来的use
module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },

webpack.config.prod.js

mode: "production",  //改为production

hmr 选项删掉

package.json

"scripts": {
    "start": "webpack-dev-server --open",
    "build": "rm -rf dist && webpack --config webpack.config.prod.js",
 },

指定yarn start时开发,使用默认的配置文件。

yarn build时生产,通过--config指定配置文件路径

继承思想

可以发现,两个配置文件除了css插件部分之外完全一样,所以根据继承的思想,把他们的共有属性放到一起。

新建一个webpack.config.base.js文件,

var HtmlWebpackPlugin = require("html-webpack-plugin");
var path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[contenthash].js",
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "demo",
      template: "src/assets/index.html",
    }),
  ],
};

webpack.config.js

var HtmlWebpackPlugin = require("html-webpack-plugin");
var path = require("path");
// require 引入
const base = require("./webpack.config.base.js");

module.exports = {
  ...base,   //把base的所有属性抄到这里
  devtool: "inline-source-map",
  devServer: {
    contentBase: "./dist",
  },        //开发需要的属性
  mode: "development",
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

webpack.config.prod.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
var path = require("path");

const base = require("./webpack.config.base.js");  //引入
module.exports = {
  ...base,    //抄base
  mode: "production",
  plugins: [
    ...base.plugins,                         //抄base的plugins属性
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash].css",
      chunkFilename: "[id].[contenthash].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: "../",
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};

总结

  1. webpack转译JS:

    不需要其他操作,只要运行webpack,就可以把src/index.js转译成dist/main.js。使用了webpack的一个内置加载器:babel-loader

  2. 生成css:两种方法

    • 使用style-loader+css-loader,生成style标签
    • 使用mini-css-extract-plugin,生成css文件(插件是n对1,如果有两个css,也会在dist目录里生成一个css文件)
  3. 生成html:

    把0个或1个html通过html-webpack-plugin生成dist/index.html。