领导力导师老韩 發表於 2022-6-16 16:33:00

如何使用Webpack打包React

<h1 id="前言">前言</h1>
<p>我们常用使用 <code>create-react-app</code> 新建 React 项目,它帮我们配置好了开箱即用的 Webpack 配置。但是如果我们要重头搭建一个使用 Webpack 打包的 React 项目,就必须要学习 Webpack 的用法了。</p>
<p>在本文中,你会了解到:</p>
<ul>
<li>Webpack 是什么</li>
<li>从开发到构建到发布发生了什么事</li>
<li>对前端项目的构建流程有一个简单的概念</li>
</ul>
<p>首先,从 Webpack 开始。</p>
<h1 id="webpack">Webpack</h1>
<p>Webpack 是一个打包工具,本文中使用到的 Webpack 版本为 <code>^5.73.0</code>。</p>
<p>当我们开发一个前端项目时,会分成很多文件,当我们要发布的时候,通常希望把它们整合成少数的几个文件,并加以混淆,压缩体积,以达到最快的传输性能。打包工具就是帮我们做这样的事。</p>
<p>在这里附上 Webpack 的中文站点:Webpack。</p>
<p>以下面的代码为例子,index.js 中引用了 bar.js 模块的代码:</p>
<pre><code class="language-js">// ./bar.js
export function bar(name) {
console.log(`in bar: ${name}`);
}

// ./index.js
import { bar } from "./bar";

const name = "from index";
bar(name);
console.log("print from index.js");
</code></pre>
<p>使用 Webpack 打包 index.js 文件:<code>webpack ./index.js</code>,默认会输出一个 main.js 文件,内容为整合过和压缩后的内容:</p>
<pre><code class="language-js">(() =&gt; {
"use strict";
console.log("in bar: from index"), console.log("print from index.js");
})();
</code></pre>
<p><strong>上面只是做示例而已,先建立起对 Webpack 的简单认知</strong>。</p>
<p>但单纯的 Webpack 还不足以我们打包 React 项目,因为 Webpack 默认只支持 js 和 json 的打包,其他的语言一律不认识,像 ts,jsx,css 等都会因为 Webpack 无法识别而打包失败。为了解决这个问题,就需要引入其他 loader 来帮助 Webpack 认识其他语言。</p>
<h2 id="loader">loader</h2>
<p>什么是 loader?就是用于帮助 Webpack 认识其他语言的库。比如,如果使用 Webpack 打包 ts 文件:<code>webpack ./index.ts</code>,会抛出如下错误:</p>
<p><img src="https://tva1.sinaimg.cn/large/005PljWlgy1h3a19h7ybwj30gx0340ux.jpg"></p>
<p>因为 Webpack 不认识 ts 文件,所以我们要引入一个帮助 Webpack 认识 ts 文件的 loader:<code>ts-loader</code>:<code>npm i -D ts-loader</code>。</p>
<p>然后在项目根目录下新建一个 <code>webpack.config.js</code> 文件,这个文件里会对 Webpack 进行各种配置,包括我们的 loader。</p>
<p>在 <code>webpack.config.js</code> 里写入:</p>
<pre><code class="language-js">module.exports = {
module: {
    rules: [{ test: /\.ts$/i, use: "ts-loader" }],
},
};
</code></pre>
<p>loader 的配置写在 module 节点下的 rules 节点中,每个 loader 的配置有 <code>test</code> 属性:说明要使用的 loader 的文件名规则,<code>use</code> 属性说明要使用哪个 loader。在上面可以看到我们配置了以 <code>.ts</code> 结尾的文件使用 <code>ts-loader</code> 解析。这样,Webpack 在遇到以 <code>.ts</code> 结尾的文件时,就会先使用 <code>ts-loader</code> 解析一遍。</p>
<p>写好配置,再使用 Webpack 打包 ts 文件,输入命令:<code>webpack ./index.ts</code>,webpack 会寻找当前目录下的 <code>webpack.config.js</code> 文件,如果有,就使用这个文件里的配置。因为我们配置了支持 ts 文件的 loader,所以是打包成功的。你能够在当前目录的 dist 文件夹下找到打包后的文件。</p>
<p>当你了解 loader 是干什么的后,同理,对于 react 的 <code>jsx</code> 和 <code>tsx</code> 文件,我们也需要安装对应的 loader 来帮助 Webpack 解析。</p>
<p>关于 loader 的更多信息,详细看这里的文档。</p>
<h2 id="plugins">plugins</h2>
<p>loader 只能解析其他语言,所以如果有 loader 无法实现的需求,就需要 plugins 来实现。</p>
<p>比如说,前端的单页面项目会有一个模板 html 文件作为入口,该 html 文件里引用了打包后的 js 文件。我们希望在打包 js 文件后自动把 html 文件也打包并引入 js,就需要 <code>HtmlWebpackPlugin</code> 这个 plugins 来实现这个功能。</p>
<p>先安装:<code>npm i -D html-webpack-plugin</code>,安装好后在 <code>webpack.config.js</code> 中引入,并在 plugins 节点下配置:</p>
<pre><code class="language-js">const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
plugins: [
    new HtmlWebpackPlugin({
      template: "index.html",
    }),
],
};
</code></pre>
<p>上面代码新实例化了一个 <code>HtmlWebpackPlugin</code> 对象到 plugins 节点数组中, <code>HtmlWebpackPlugin</code> 对象接收参数 <code>template</code>,执行了 html 模板的路径,这里要确保该路径下有这个 html 文件,比如内容为:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;Webpack App&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Hello world!&lt;/h1&gt;
    &lt;h2&gt;Tip: Check your console&lt;/h2&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>webpack 打包后,在 <code>dist</code> 文件夹下找到 <code>index.html</code> 和打包后的 <code>main.js</code>,其中 <code>index.html</code> 内容如下:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;Webpack App&lt;/title&gt;
    &lt;script defer src="main.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Hello world!&lt;/h1&gt;
    &lt;h2&gt;Tip: Check your console&lt;/h2&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>能看到引入了 <code>main.js</code> 文件,这个 html 就成了我们单页应用的入口。</p>
<p>关于 <code>HtmlWebpackPlugin</code> 更多的配置看这里。</p>
<p>希望上面的讲解能够让你稍微了解到构建过程中发生了什么事,简单来说,就是将开发的代码精简成几个文件,对不同的项目会有不同的配置。</p>
<h1 id="搭建-react">搭建 React</h1>
<p>那么对 React 项目应该有什么配置呢?这里的 React 版本是 18.</p>
<p>首先确保有能够运行 React 的环境,所以确保安装了 <code>react</code> 和 <code>react-dom</code> 依赖。接着,从上面得知,因为 Webpack 不认识 js 和 json 以外的文件,所以要安装帮助它认识 <code>jsx</code>,<code>tsx</code> 文件的 loader。</p>
<p>这里我们使用 <code>babel</code> 来认识。由于要在 Webpack 里使用 <code>babel</code> loader,所以也要安装 <code>babel-loader</code>。<code>babel-loader</code> 要在 node 里使用转译功能,需要安装 <code>babel-core</code>,要转义 React 文件,所以要安装 <code>@babel/preset-react</code> 供 babel 使用,如果你使用 ts 编写,那么还要安装 <code>@babel/preset-typescript</code> 让 babel 拥有处理 ts 的能力。</p>
<p>总的来说,安装以下依赖:</p>
<pre><code class="language-bash">npm i -D babel-loader babel-core @babel/preset-react @babel/preset-typescript
</code></pre>
<p>以上几个库,我希望你能够充分理解它们各自的作用。</p>
<p>由于我们决定使用 babel 来处理 ts 文件,所以上面的 <code>ts-loader</code> 就不在需要了,将它从依赖和 <code>webpack.config.js</code> 中去掉。</p>
<p>在 <code>webpack.config.js</code> 中修改为如下 loader:</p>
<pre><code class="language-js">{
module: {
    rules: [
      {
      test: /\.(js|jsx|ts|tsx)$/,
      loader: "babel-loader",
      exclude: /node_modules/,
      }
    ],
},
}
</code></pre>
<p>如上,我们将结尾为 <code>js</code>,<code>jsx</code>,<code>ts</code>,<code>tsx</code> 的文件都交给了 <code>babel-loader</code> 处理。这里我们使用了 <code>loader</code> 节点而不是 <code>use</code> 节点,这两者都是 <code>use: [{ loader: 'xxx-loader' }]</code> 的简写。添加了 <code>exclude</code> 节点,意思是符合这个规则的文件、文件夹不使用这个 loader 处理。</p>
<p>接着要配置 babel,以告诉 babel 要这么处理这几个文件。在根目录下新建 <code>.babelrc</code> 文件,babel 会使用这个文件里的配置。</p>
<pre><code class="language-json">{
"presets": ["@babel/preset-react", "@babel/preset-typescript"]
}
</code></pre>
<p>在 <code>presets</code> 节点中配置我们的转译,<code>@babel/preset-typescript</code> 用于处理 ts 文件,<code>@babel/preset-react</code> 用于处理 React 文件。</p>
<p>到这里,Webpack 关于 React 的简单配置就可以了。但是为了让项目能够跑起来,还得做一点其他的配置。</p>
<h2 id="其他配置">其他配置</h2>
<p>下面的各种配置,不在本文的讲解范围内,希望你能够自己去了解。</p>
<p>在 <code>webpack.config.js</code> 下添加 <code>resolve</code> 节点,节点下的 <code>extensions</code> 数组会告诉 Webpack 如果遇到导入文件没有指定后缀名的话,从这个数组里的后缀名里逐个寻找。</p>
<pre><code class="language-js">{
resolve: {
    extensions: [".tsx", ".ts", ".jsx", ".js", "..."],
}
}
</code></pre>
<p>为了支持 ts 语法,需要在根目录下新建 <code>tsconfig.json</code> 文件,内容如下:</p>
<pre><code class="language-json">{
"compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "module": "es6",
    "target": "es5",
    "allowJs": true,
    "jsx": "react"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
</code></pre>
<p>在 <code>src</code> 目录下建立三个代码文件:<code>main.tsx</code>,<code>App.tsx</code>,<code>foo.ts</code>,内容分别如下:</p>
<pre><code class="language-js">//main.tsx
//项目入口
import ReactDOM from "react-dom/client";
import React from "react";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(&lt;App /&gt;);

//App.tsx
//App 组件
import React from "react";
import { print } from "./foo";

export default function App() {
React.useEffect(() =&gt; {
    print("in App component");
}, []);
const = React.useState(0);
return (
    &lt;div&gt;
      APP
      &lt;div&gt;{count}&lt;/div&gt;
      &lt;button
      onClick={() =&gt; {
          setCount(count + 1);
      }}
      &gt;
      Btn
      &lt;/button&gt;
    &lt;/div&gt;
);
}

//foo.ts
//ts 代码
export function print(value: string) {
console.log(value);
}
</code></pre>
<p>需要安装 React 的声明文件:<code>npm i -D @types/react-dom</code>,不然找不到各种类型。</p>
<h1 id="打包">打包</h1>
<p>最终,我们可以执行 <code>webpack ./src/main.tsx</code> 打包 React 项目了,这里选择了 <code>./src/main.tsx</code> 文件作为打包的入口,打包完成后,你会在根目录的 <code>dist</code> 文件夹下看到打包后的文件:</p>
<p><img src="https://tva1.sinaimg.cn/large/005PljWlgy1h3a5bzomntj308a02xmxb.jpg"></p>
<p>在浏览器中打开 <code>index.html</code> 文件,能够正常运行,即说明打包成功了。<code>dist</code> 文件夹内的内容就是我们最终要发布的文件。运行起来看看吧。</p>
<h1 id="总结">总结</h1>
<p>本文只是简单介绍 Webpack 的功能而已,还有很多常用的功能没有介绍,打包后的文件也有很多问题,比如说,代码没有混淆,体积太大,足有 1.16M!这个问题参考这篇文档可以解决。</p>
<p>更多的配置还是希望你能够自己去找到,毕竟,我不是想帮你们解决问题,而是想教你们如何解决问题。</p>
<p>告辞。</p>
<h1 id="参考">参考</h1>
<p>Webpack 中文文档 by Webpack</p>
<p>webpack 深入了解之 loader 配置详解(一) by 致我逝去的青春</p>
<p>Issue: ReferenceError: React is not defined by yoyo837</p><br><br>
来源:https://www.cnblogs.com/dvorakchen/p/16382467.html
頁: [1]
查看完整版本: 如何使用Webpack打包React