第1期 webpack基本打包

361 阅读3分钟

npm init

一般我们会用到npm里面的东西,执行以下命令生成一个package.json文件,当你安装npm包的时候会同步更新到package.json,别人拉你的项目的时候直接npm install就可以安装所有需要的npm模块,方便省事。

npm init

然后目录下有一个package.json文件

安装webpack,webpack4+还要装一个webpack-cli,因为webpack4+分离一部分出来了,据说是为了更好地管理。

npm install webpack -D
npm install webpack-cli -D

.gitignore

新建一个.gitignore文件,把node_modules写入,这个文件是指提交git的时候忽略哪些文件。此处有一个疑问,那引入的npm包要从哪里来?其实是在打包的时候就会把用到的(不是全部)npm包里面的东西打包进去。

bundle chunks module

粗浅理解,我们编写的源文件里面会引入多个module,被webpack处理生成对应的多个chunks,chunks组成bundle,bundle就是打包后的文件,一个bundle包含多个chunks。

npx

npx解决的问题

当我们安装一个模块,可以通过以下方式执行

(1)package.json的script添加命令"server":"webpack",然后在命令行通过npm run server执行。

(2)命令行执行node-modules/.bin/webpack --version

(3)npx webpack

npx是Node自带npm模块 让项目内部安装的模块用起来更方便,npx 还能避免全局安装的模块,代码运行时,npx将模块下载到一个临时目录 使用以后再删除。

npx的原理就是运行的时候会到node_modules/.bin路径和环境变量里面 检查命令是否存在,由于npx会检查环境变量,所以系统命令也可以调用Bash 内置的命令不在$PATH里面,所以不能用。

npx一些参数

npx --no-install webpack // 强制使用本地模块不使用远程模块
npx --ignore-existing webpack // 强制使用远程模块不使用本地模块
npx node@0.12.8 -v // 指定安装模块的版本 并执行命令-v
npx -p node@0.12.8 node -v // 指定安装模块的版本 并执行命令-v
npx -p lolcatjs -p cowsay [command] // 安装多个模块 并执行命令[command]
npx -p lolcatjs -p cowsay -c 'cowsay hello | lolcatjs' -c作用是'cowsay hello | lolcatjs'都由npx执行 否则默认第一个由npx执行 第二个由Shell执行
npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32 // 执行远程的代码。

尝试一次最简单的webpack打包

添加html和js文件

新建文件 index.html、index.js。

#index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>index html</title>
</head>
<body>
  <div class="red">这里是index.html</div>
  <script src="./index.js"></script>
</body>
</html>
#index.js
console.log("这里是index.js")

打开html,一切正常。

添加webpack.config.js文件并配置

新建webpack.config.js文件。

#webpack.config.js
const path = require('path');
module.exports = {
  mode: "development",//必加
  //相对于process.cwd(),process.cwd()返回的是Node.js进程的当前工作目录,此处是workplace。
  entry: "./src/pages/main/index/index.js",
  output: {
    filename: "js/[name].[hash:8].js",//js bundle的文件名
    path: path.resolve(__dirname, "../dist"),//所有类型bundle的路径 绝对路径
  },
};

命令行输入以下命令开始打包

npx webpack --config webpackConfig/webpack.config.js

这里仅仅只有一个js文件是因为webpack只能识别.js的文件,如果要识别类似css、html等的文件需要再另外配置。

clean-webpack-plugin 打包前删除dist

如果我修改了index.js文件的内容,我再进行打包,就会出现两个哈希不一样的js文件。

#index.js
console.log("这里是修改后的index.js")

之前那一份其实是不需要的,我们来设置一下,每次打包前删除dist文件夹。

npm install clean-webpack-plugin -D
#webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");//引入插件
module.exports = {
  mode: "development",
  entry: "./src/pages/main/index/index.js",
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [
    new CleanWebpackPlugin()//使用插件
  ],
};

entry的四种格式

// 字符串 单页应用(SPA)
entry:"src/pages/main/index/js/index.js"
// 数组 多对一
entry:['src/pages/main/index/js/index.js','src/pages/main/list/js/index.js']
// 对象 多页应用(MPA)
entry:{
    'pages/main/index': 'src/pages/main/index/js/index.js',
    'pages/main/list': 'src/pages/main/list/js/index.js'
}
// 函数 返回以上三种格式
entry:()=>{return xxx;}

尝试打包一个多页应用

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "development",
  // 用对象的形式输入entry
  entry: {
    "pages/main/index": "./src/pages/main/index/index.js",
    "pages/main/list": "./src/pages/main/list/index.js",
  },
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [new CleanWebpackPlugin()],
};

这里的name为pages/main/index,结合output的path和filename,最终bundle的路径如下图。

context

设置entry的根路径context可以少写一些重复路径

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  mode: "development",
  context:path.resolve(__dirname,"../src/pages"),//绝对路径
  entry: {
    "pages/main/index": "./main/index/index.js",//相对context的路径设置
    "pages/main/list": "./main/list/index.js",
  },
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [new CleanWebpackPlugin()],
};

多页应用入口 自动化

  entry: {
    "pages/main/index": "./main/index/index.js",//相对context的路径设置
    "pages/main/list": "./main/list/index.js",
  },

如果只有一两个入口我们可以手动写,但是如果有十几个或者二十几个页面,手动写就有点麻烦了。所以我们可以通过一个函数来对路径进行处理并返回以上的格式。

新建一个文件util.js

#util.js
const fs = require("fs");
const path = require("path");

/**
 * @desc: entry路径处理
 */
exports.entryFilePath = function () {
  let entryFilePath = {};
  // src/pages里面的文件夹
  page_level_one_arr = fs.readdirSync(path.join(__dirname, "../src/pages"));
  page_level_one_arr.forEach((page_level_one) => {
    // mac 自动生成的.DS_Store文件 不进行处理
    if (page_level_one == ".DS_Store") return;
    // src/pages/main里面的文件夹
    page_level_two_arr = fs.readdirSync(
      path.join(__dirname, "../src/pages", page_level_one)
    );
    page_level_two_arr.forEach((page_level_two) => {
      // mac 自动生成的.DS_Store文件 不进行处理
      if (page_level_two == ".DS_Store") return;
      // 存储一个目录的路径
      entryFilePath[
        `pages/${page_level_one}/${page_level_two}`
      ] = `./${page_level_one}/${page_level_two}/index.js`;
    });
  });
  return entryFilePath;
};

以上方法是处理如图的路径,其他路径需要另外处理。至此自动打包js下的文件并按预期目录输出打包后的文件。

npm run build设置

每次都在命令行输入npx webpack --config webpackConfig/webpack.config.js进行打包有点麻烦,我们可以在package.json进行设置。

以后可以通过npm run build进行打包啦~

html打包

如上可以发现仅仅是对JS文件进行打包,在html中想要引用还要手动设置路径。

删除script引入

#index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>index html</title>
</head>
<body>
  <div class="red">这里是index.html</div>
  // 删除该句
  <!--<script src="../../../../dist/js/pages/main/index.848d2746.js"></script>-->
</body>
</html>

下面我们安装html-webpack-plugin插件,对html进行打包,让其自动引入对应的js文件。

npm install html-webpack-plugin -D
#webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { entryFilePath } = require("./util.js");
const htmlWebpackPlugin = require("html-webpack-plugin");// 引入插件

module.exports = {
  mode: "development",
  entry: entryFilePath(),
  context: path.join(__dirname, "../src/pages"),
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [
    new CleanWebpackPlugin(),
    new htmlWebpackPlugin({
      //设置html源文件路径
      template: path.join(__dirname, `../src/pages/main/index/index.html`),
      //设置bundle路径
      filename: `pages/main/index.html`,
      // 设置引入哪个chunks 不写默认全部chunks
      chunks: [`js/pages/main/index`],
    }),
  ],
};

打包后的html文件会自动引入的chunks 匹配路径
跟entry一样的问题,每设置一个页面我们就要设置new一次htmlWebpackPlugin,所以写一个方法,自动化new htmlWebpackPlugin。

#util.js
/**
 * @desc: htmlWebpackPlugin 路径处理
 */
exports.htmlWebpackPluginPath = function () {
  let htmlWebpackPluginPath = [];
  // src/pages里面的文件夹
  page_level_one_arr = fs.readdirSync(path.join(__dirname, "../src/pages"));
  page_level_one_arr.forEach((page_level_one) => {
    // mac 自动生成的.DS_Store文件
    if (page_level_one == ".DS_Store") return;
    // src/pages/main里面的文件夹
    page_level_two_arr = fs.readdirSync(
      path.join(__dirname, "../src/pages", page_level_one)
    );
    page_level_two_arr.forEach((page_level_two) => {
      // mac 自动生成的.DS_Store文件
      if (page_level_two == ".DS_Store") return;
      // 生成一个htmlWebpackPlugin
      htmlWebpackPluginPath.push(
        new htmlWebpackPlugin({
          template: path.join(
            __dirname,
            `../src/pages/${page_level_one}/${page_level_two}/index.html`
          ),
          filename: `pages/${page_level_one}/${page_level_two}.html`,
          chunks: [`pages/${page_level_one}/${page_level_two}`],
        })
      );
    });
  });
  return htmlWebpackPluginPath;
};
#webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { entryFilePath, htmlWebpackPluginPath } = require("./util.js");//引入方法

module.exports = {
  mode: "development",
  context: path.join(__dirname, "../src/pages"),
  entry: entryFilePath(),
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  plugins: [
    new CleanWebpackPlugin(), 
    ...htmlWebpackPluginPath()//引入方法
  ],
};

加载CSS

内联样式表

目前仅有html和js两种格式,我们来添加css的文件。

#index.css
.red{  color: red;}

在js文件中引入css文件

import './index.css';//引入css文件
console.log("这里是index.js2")

安装css-loaser和style-loader

npm install css-loader style-loader -D

配置webpack.config.js文件

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { entryFilePath, htmlWebpackPluginPath } = require("./util.js");

module.exports = {
  mode: "development",
  context: path.join(__dirname, "../src/pages"),
  entry: entryFilePath(),
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  module: {
    rules: [
      {
        test: /\.css$/,//匹配.css的文件
        use: [
            "style-loader",//样式动态的以<style>形式插入
            "css-loader"//识别.css模块
        ],
      },
    ],
  },
  plugins: [new CleanWebpackPlugin(), ...htmlWebpackPluginPath()],
};

打包后并没有产生.css文件,因为不是以link的方式引入,而是通过js动态的将样式插入到head中。

外部样式表

npm i mini-css-extract-plugin -D
#webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");//引入插件
const { entryFilePath, htmlWebpackPluginPath } = require("./util.js");

module.exports = {
  mode: "development",
  context: path.join(__dirname, "../src/pages"),
  entry: entryFilePath(),
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,//删除style-loader 引入loader
          },
          "css-loader",
        ],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    ...htmlWebpackPluginPath(),
    //引入插件
    new MiniCssExtractPlugin({
      filename: "css/[name].[hash:8].css",
    }),
  ],
};

js文件自动会引入外部样式表

加载图片

在css中 || 在js中 || 在html中 引入图片

npm i file-loader url-loader -D
#index.html
css中引入的图片
<div class="red">这里是index.html</div>
JS中引入的图片
<img id="J-img" src="/"></img>
html中引入的图片
<img src="./img/img.png" alt="">
<img src="~global/img/global.jpg" alt="">
#index.css
.red{
  color: red;
  background:url("~global/img/global.jpg");
  // background:url("./img/img.png");
}
#index.js
import './index.css';
import img from "global/img/global.jpg";
console.log("这里是index.js2");
document.getElementById("J-img").src = img;
#webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { entryFilePath, htmlWebpackPluginPath } = require("./util.js");

module.exports = {
  mode: "development",
  context: path.join(__dirname, "../src/pages"),
  entry: entryFilePath(),
  output: {
    filename: "js/[name].[hash:8].js",
    path: path.resolve(__dirname, "../dist"),
    publicPath: "../../",//设置全局默认的公共路径
  },
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: {
          loader: "url-loader",
          options: {
            limit: 1,
            name: "global/img/[name].[ext]",
            esModule: false,
            //针对所有图片也可以设置路径
          },
        },
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: "../../../",//css内部的资源的路径处理
            },
          },
          "css-loader",
        ],
      },
      // html的img路径处理
      {
        test: /\.(html)$/,
        use: {
          loader: "html-loader",
          options: {
            attributes: true,
          },
        },
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    ...htmlWebpackPluginPath(),
    new MiniCssExtractPlugin({
      filename: "css/[name].[hash:8].css",
    }),
  ],
  resolve: {
    alias: {
      global: path.join(__dirname, "../src/global"),
    },
  },
};

总结:

1、可以用output的publicPath来设置所有的默认引入路径

2、每个loader或者plugin都可能有自己的publicPath,可以去查找相关资料

3、其实会和乱,用../../类似的地址的时候,以上可以作为一个参考,接下来我们要开始本地开发,设置publicPath,再根据相对workplace设置路径会清晰很多。

本地 && 打包

"build": "webpack",
"server": "webpack-dev-server"
"watch":"webpack --watch"

npm run build

就是执行打包 将文件打包并生成dist文件夹

npm run watch

就是监听本地文件更改 打开文件index.html (file:///Users/TEST/src/pages/main/index/tmpl/index.html) 更改index.html的内容 刷新页面 就能看到更改

npm run server

npm i webpack-dev-server -D

启动一个本地服务器 当通过localhost:8081访问该服务器 服务器返回的内容可通过contentBase设置

devServer: {
    contentBase: "src",
    port: 8081
},

终端会输出一些提示

// 访问contentBase的地址
ℹ「wds」: Project is running at http://localhost:8081/

// 指output中设置的publicPath 指output的路径为/ 
ℹ「wds」: webpack output is served from /

// 指devServer的contentBase 即服务器返回的内容
ℹ「wds」: Content not from webpack is served from src    

至此 基本的webpack已经告一段落。之后再有其他的用到的点再更。