webpack从0到1

153 阅读4分钟

概览

webpack是模块化方案的一个工具手段。可以把浏览器不认识的文件代码翻译成认识的3大件!webpack把一切静态资源都视为模块,所以又叫静态资源打包器。

如果把webpack视为一个函数,那么webpack.config里的配置项,就可以视为这个函数的一系列参数,我们通过修改参数来控制输出结果!

weback 也是微内核的架构,核心自带的功能轻量化、很多功能都是通过一些列loader 、plugin进行扩展。

机制

通过入口文件,按照引入的依赖关系递归构建好依赖图谱,在这个过程中,遇到不同的文件类型,调用你配置好的loader进行翻译处理,最后输出浏览器可执行的代码文件。

HRM

"Hot Module Replacement" 热更新。

在开发环境中,为了提升开发效率和体验,我们希望修改源码实时且无刷新地反馈在浏览器中,这种技术就叫做HRM。

loader

  1. webpack 本身只能处理js模块,要处理其他文件类型,就需要不同的loader。
  2. 不需要单独引入,但是依赖里也是要安装的,是自动加载的
  3. 针对特定格式文件进行处理,test 匹配后用use 什么loader
  4. 类似gulp的管道机制,也就是接受源码,处理后要返回源码,供给下一个loader去处理,是链式的过程。

plugin

  1. 需要引入、实例化new,可以实例化多个。
  2. 是打包加工源码过程中某个钩子中触发执行的。webpack 提供了几十个钩子,够你植入插件逻辑。是不区分文件类型的,只是不同钩子时期的任务。

从0->1配置

现在我们不依赖任何脚手架,自己从0一点点搭建一个模块化形式的项目。本文将搭建react类型项目。

安装webpack

创建文件夹如 webpack,并在终端内执行

$ npm init 一路回车初始化项目
$ npm i webpack  webpack-cli -D

默认安装的是最新版本。目前是:

webpack ^5.58.1

webpack-cli ^4.9.0

创建项目结构

webpack/
src/

index.js

index.html

package.json

各文件内容

1.index.js

function test(content) {

  document.querySelector('#app').innerHTML = content;

}

 

test('something');

2.index.html

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>webpack</title>

  </head>

  <body>

    <div id="app"></div>

  </body>
  <script type="text/javascript" src="./output/main.js"></script>

</html>

output/main.js是打包后的js代码路径。

3.package.json

{

  "name": "webpack-study",

  "version": "1.0.0",

  "description": "",

  "main": "src/index.js",

  "scripts": {

    "test": "..."

  },

  "author": "",

  "license": "ISC",

  "devDependencies": {

    "webpack": "^5.58.1",

    "webpack-cli": "^4.9.0"

  }

}

补充打包命令,即对 ./src/index.js 文件进行编译,输出目录为output,指定开发环境参数和devtool类型可以不对产物进行压缩,便于后续的产物分析。

"scripts": {

  "build": "webpack ./src/index.js -o ./output --mode=development --devtool=cheap-module-source-map"

}

执行 npm run build 命令后,输出到output/main.js。 这时候浏览器访问index.html 就可以看到something了!

从 1->2 配置

在刚才从0->1的基础上,进一步优化升级我们的配置。

一、新建webpack.config.js,将build命令执行的参数转移到该文件:

1.webpack.config.js

const path = require('path');

 

module.exports = {

  mode: 'development',

  devtool: 'cheap-module-source-map',

  entry: './src/index.js',

  output: {

    path: path.resolve(__dirname, 'output'),

    filename: 'main.js'

  },

}

2.修改package.json:

"scripts": {

  "build": "webpack"

}

执行命令  yarn build,效果一样。

二、集成各种能力

1.增加ES6的转换能力

1.1 创建 src/es6.js文件
里面是一堆es6的语法,我们要用babel 对他们进行翻译。

1.2 安装:
$ npm i @babel/core @babel/preset-env babel-loader -D

1.3 配置
webpack.config.js

module: {

    rules: [{

      test: /\.js$/,

      use: {

        loader: 'babel-loader',

        options: {

          presets: [

            ['@babel/preset-env']

          ],

        }

      }

    }]

  }

1.4 再次打包
查看output/main.js,发现es6语法得到转换了。

2.引入react相关配置

2.1 安装:
npm i react react-dom @babel/preset-react -S

2.2 创建:
src/react.js

2.3 写入:

import React from 'react';

import { render } from 'react-dom';

 

const App = () => <div>App</div>;

 

render(<App />, document.querySelector('#app'));

2.4 配置: webpack.config.js

entry: './src/react.js'// 修改文件入口

// module.rules[0].use.options.presets

presets: [

   ...

   '@babel/preset-react'

],

2.5 打包: 打包查看HTML文件

3.缓存包提取

optimization: {

    splitChunks: {

      cacheGroups: {

        vendor: {

          filename: 'vendor.js',

          chunks: 'all',

          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/

        },

      }

    }

  }

然后记得把产物引入 html文件。

4.css文件处理

4.1 安装:

npm i mini-css-extract-plugin -D
npm i style-loader css-loader -D

4.2 配置:
webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

...

plugins: [

    new MiniCssExtractPlugin({

      filename: "[name].css",

      chunkFilename: "[id].css"

    })

  ],

...

// module.rules
// MiniCssExtractPlugin.loader 代替 style-loader

4.3 引入:

自动引入脚本和样式表

虽然上面的样式抽离成功了,但是并没有出现在 HTML文件中,我们依然要像之前的 js 文件一样手动引入。对于动态生成的文件,这显然是不现实的。于是:有了5中优化。

5.html模板

从4中暴露的问题:动态生成的css、js的文件产物,我们要手动在html中去引入,显然不够智能。所以我们希望,可以自动插入。

5.1 安装:

$ npm html-webpack-plugin -D

5.2 配置:

const HtmlWebpackPlugin = require('html-webpack-plugin')

...

plugins:[

    new HtmlWebpackPlugin()

],

效果是打包文件多了一个index.html

5.3 模板:

问题是缺少 div#app 元素,所以我们使用模板文件:

plugins:[

    new HtmlWebpackPlugin({

   template: './template.html'

    })

]

6.HMR

到目前为止,我们一直在使用打包+刷新的模式查看代码效果,显然十分繁琐低效。借助本地开发服务器来解决这个问题:

6.1 安装:

$ npm i webpack-cli webpack-dev-server -D

 

6.2 增加命令:


"scripts": {

    "start": "webpack serve"

 }

6.3 启动看效果:

我们修改App组件,可以看到页面已经实时更新了,只不过是【刷新】!继续:

 

webpack.config.js:

{

devServer: {

  port: 8000, // 顺便更改一下端口

  hot: true

}

}

入口文件:



  ...

render(<App />, document.querySelector('#app'));

 

if (module.hot) {

  module.hot.accept(App, () => {

    render(<App />, document.querySelector('#app'));

  });

}

// 如果 App 组件是外部文件创建的,通常写作(与import导入的路径一致):

if (module.hot) {

  module.hot.accept('./App', () => {

    render(<App />, document.querySelector('#app'));

  });

}

至此,一个功能相对完善的脚手架就搭建完成了!