通过webpack从零开始搭建React项目

webpack5搭建react框架-antd组件库使用 antd组件库在react项目中的使用。 阅读详情

从零开始搭建React项目

1. Webpack基础概念

webpack主要作用:

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
  • 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
  • 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
  • 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

Webpack 有以下几个核心概念:

  • Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
  • Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  • Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
  • Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
  • Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
  • Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。

构建过程:

  1. 从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有 Module。
  2. 每找到一个 Module, 就会根据配置的 Loader 去找出对应的转换规则。
  3. 对 Module 进行转换后,再解析出当前 Module 依赖的 Module。
  4. 这些模块会以 Entry 为单位进行分组,一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk。
  5. 最后 Webpack 会把所有 Chunk 转换成文件输出。
  6. 在整个流程中 Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。

2. 项目需求

使用Webpack 4.x 搭建项目,满足以下需求:

  1. 使用ES6语言
  2. 使用React框架
  3. 自动生成HTML
  4. webpack-dev-server
  5. 加载样式(CSS、SCSS)
  6. 加载静态资源(图片、字体)
  7. 使用第三方库
  8. 其他(clean,merge,source map)

3. 实战

3.1 基础环境

NodeJS版本10.4.1。

  1. 初始化生成一个 package.json 文件。

    npm init -y
    
  2. 本地安装webpack

    webpack4版本命名行相关的功能独立到Webpack-cli。

    npm i webpack webpack-cli -D
    
  3. 新建webpack.config.js 配置文件。

    webpack默认会查找 webpack.config.js 作为配置文件。
    
    自定义配置文件名称:
    webpack --config xx.js
    
  4. 基础配置

    webpack配置采用commonJS规范,通过module.export导出一个描述如何构建的 Object 对象。
    
    module.exports={
        entry:{},     //入口配置*
        output:{},    //出口配置*
        modules:{},   //module.rules loader
        plugins:[],   //插件
        devServer:{}   //开发服务器
    }
    

3.2 ES6

3.2.1 前置步骤
  1. 按如下结构创建相应文件与目录:

    │  package.json
    │  webpack.config.js
    ├─dist
    │      index.html        
    └─src
            main.js
    
  2. main.js 内容:

    class Ui {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
    
        coding() {
            return `${this.name} is coding`;
        }
    }
    
    let ui= new Ui('hqz','18');
    
    document.getElementById('app').innerText=ui.coding();
    
  3. index.html 内容:

    <html>
    <head>
    	<meta charset="UTF-8">
    	<title>ES6</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="bundle.js"></script>    //手动引入打包后的JS
    </body>
    </html>
    
  4. webpack配置

    const path=require("path");
    
    module.exports={
        entry:'./src/main.js',     //入口文件
        output:{                   //出口配置
            filename: 'bundle.js', //出口文件名
            path: path.resolve(__dirname, 'dist')  //出口文件路径
        },
        module: {},    //module.rules loader
        plugins:[],    //插件
        devServer:{}   //开发服务器
    };
    
3.2.2 配置Babel

目前浏览器对ES6的标准支持不全,所以我们需要把ES6转换成ES5,包含以下两件事:

  1. 把新的 ES6 语法用 ES5 实现。
  2. 给新的 API 注入 polyfill。

babel可以方便的完成以上2件事。 babel-preset-env的工作方式类似于babel-preset-latest,但它允许您指定环境并仅转换该环境中缺少的功能。

babel 7.X的主要变更:

  • 删除对未维护的 Node 版本的支持:0.10,0.12,2,5
  • 删除“Stage” & 年度预设(preset-es2015 等), 用@babel/preset-env 取代。
  • 对部分软件包进行重命名(e.g. babel-core–>@babel/core)

简单升级:

  1. 利用babel-upgrade

    npm i babel-upgrade -g
    babel-upgrade --write
    
  2. 重新安装包

  3. 修改配置文件中的包名

配置:

  1. 本地安装Babel

    npm i -D @babel/core @babel/preset-env babel-loader
    @babel/plugin-transform-object-rest-spread @babel/plugin-transform-export-extensions @babel/plugin-transform-class-properties @babel/plugin-syntax-dynamic-import
    
  2. 配置webpack

    在module.rules中添加如下:
    module: {
            rules: [{
                test: /\.js$/,    //使用正则匹配所有需要使用babel-loader的文件
                use: {
                    loader: "babel-loader",  //指明要使用的loader
                    options: {               //传入loader的参数
                        presets: [           //用于解析一组语法特性
                            [
                                "@babel/preset-env",       //包含当前所有 ECMAScript 标准里的最新特性
                                {
                                    "targets": {   //指定需要兼容的浏览器类型和版本
                                        "browsers": [
                                            "> 1%",     //支持市场份额超过1%的浏览器。
                                            "ie >= 9"   //支持IE9以上的版本
                                        ]
                                    }
                                }
                            ]
                        ],
                        plugins: [         //用于解析某个语法特性
                            "@babel/plugin-proposal-object-rest-spread", //解析对象的扩展运算符(ES2018)
                            "@babel/plugin-proposal-export-default-from",  //解析额外的export语法:export v from "xx/xx"
                            "@babel/plugin-proposal-export-namespace-from", //解析额外的export语法:export v as vv from "xx/xx";
                            "@babel/plugin-proposal-class-properties",   //解析class中的静态属性
                            "@babel/plugin-syntax-dynamic-import"         //解析import方法
                        ]
                    }
                }
    
            }]
        },
    
  3. 执行编译

    package.json 的 script字段添加如下:
    "build": "webpack"
    
    执行 npm run build
    

    编译完成后,dist文件夹下多出一个bundle.js文件,打包成功。

3.3 React

3.3.1 前置步骤
  1. 本地安装react

    npm i -D react react-dom
    
  2. mian.js

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    
    class App extends Component {
        render() {
            return <h1>Hello word!</h1>
        }
    }
    ReactDOM.render(<App />, document.getElementById('app'));
    
3.3.2 配置Babel

Babel也可用于解析JSX,需要使用babel-preset-react。

  1. 本地安装babel-preset-react

    npm i -D @babel/preset-react
    
  2. 配置webpack (在ES6环境的基础上)

    在module.rules中添加如下:
    module: {
            rules: [{
                test: /\.js$/,   
                use: {
                    loader: "babel-loader", 
                    options: {               
                        presets: ["@babel/preset-env","@babel/preset-react"]  //用于解析ES6+React
                    }
                }
    
            }]
        },
    

3.4 自动生成HTML

3.4.1 单个页面

使用html-webpack-plugin 自动生成HTML,并引入相应文件。

  1. 将index.html移到src目录下,并重命名:

    │  package.json
    │  webpack.config.js
    ├─dist     
    └─src
            main.js
            template.html
    
  2. template.html 内容:

    <html>
    <head>
    	<meta charset="UTF-8">
    	<title>自动生成HTML</title>
    </head>
    <body>
        <div id="app"></div>     //去掉手动引入script
    </body>
    </html>
    
  3. 本地安装

    npm i -D html-webpack-plugin
    
  4. webpack配置

    const HtmlWebpackPlugin = require('html-webpack-plugin');  //引入插件
    
    plugins:[
        new HtmlWebpackPlugin({
        	template:'./src/template.html',  //html模板
        })
    ]
    
  5. 编译

  6. 编译后生成的HTML

    <html>
    <head>
      <meta charset="UTF-8">
      <title>自动生成HTML</title>
    </head>
    <body>
    <div id="app"></div>
    <script type="text/javascript" src="bundle.js"></script>   //自动引入JS文件
    
    </body>
    </html>
    
3.4.2 多个页面

如果单页应用中需要多个页面入口,或者多页应用时配置多个html时,那么就需要实例化该插件多次。

  1. 目录结构要求:

    │  package.json
    │  webpack.config.js
    │  template.html
    ├─dist
    └─src                   
        ├─A                 
        │      A.js 
        └─B
               B.js
    
  2. 配置webpack

    const HtmlWebpackPlugin = require('html-webpack-plugin');  //引入插件
    
    entry: {                           //多入口使用对象形式配置,chunk名称为key值
            A: './src/pages/A/A.js',
            B: './src/pages/B/B.js',
    },
    output:{                   
            filename:'[name].bundle.js',  //[name]代表chunk名称
            path: path.resolve(__dirname, 'dist')
    },
        
    plugins:[
        new HtmlWebpackPlugin({
            chunks: ['A'],           //要引入的chunk
            filename:'A.html',       //生成的文件名
            template:'template.html',  //模板文件
        }),
        new HtmlWebpackPlugin({
            chunks:['B'],
            filename:'B.html',
            template:'template.html'
        }),
    ]
    
  3. 编译后的目录结构

    │  package.json
    │  webpack.config.js
    │  template.html
    ├─dist
    |	A.bundle.js
    |	A.html
    │  	B.bundle.js
    │  	B.html
    └─src                   
        ├─A                 
        │      A.js 
        └─B
               B.js
    

3.5 Webpack-dev-server

webpack-dev-server提供了一个简单的服务器,用于访问 webpack 构建好的静态文件,我们日常开发时可以使用它来调试前端代码。 webpack-dev-server将构建好的项目存在内存中。

DevServer 支持模块热替换, 可在不刷新整个网页的情况下实时预览页面。 原理是当一个源码发生变化时,只重新编译发生变化的模块,再用新输出的模块替换掉浏览器中对应的老模块。

3.5.1 devServer
  1. 本地安装devServer

    npm i -D webpack-dev-server
    
  2. 配置webpack:

    const webpack = require('webpack');
    
    plugins: [
            new webpack.HotModuleReplacementPlugin()  //启用 HMR (webpack 4)
    ],
    
    devServer:{
            hot: true,                 //开启模块热替换
            contentBase: './dist',     //将dist目录下的文件,作为额外可访问文件
            host: '0.0.0.0',           //DevServer 服务监听的地址,默认是localhost。当需要同个局域网可访问你的服务时,可设成0.0.0.0
            port: 3000,                //DevServer 服务监听的端口,默认8080
            https: false,              //是否使用HTTPS服务
            open: true                 //自动打开网页,地址是host:port
    },
    
    只有在通过 DevServer 去启动 Webpack 时配置文件里 devServer 才会生效,因为这些参数所对应的功能都是 DevServer 提供的,Webpack 本身并不认识 devServer 配置项。 
    
  3. 执行编译

    package.json 的 script字段添加如下:
    "start": "webpack-dev-server"
    
    执行 npm start
    
  4. http://192.168.1.87:3000/A.html

3.5.2 open-browser-webpack-plugin
  1. 本地安装

    npm i -D open-browser-webpack-plugin
    
  2. 配置webpack

    const OpenBrowserPlugin = require('open-browser-webpack-plugin');
    
    plugins: [
        new OpenBrowserPlugin({ url: 'http://192.168.1.87:3000/A.html' })  //开启服务后,自动打开的地址
    ]
    
  3. 执行 npm start后,会自动打开http://192.168.1.87:3000/A.html页。

3.6 加载样式

webpack本身只认得JS文件,其他非JS文件需要用loader进行转换。

3.6.1 加载CSS

处理css文件,需要用到以下两个loader:

  • css-loader 负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 @import 和 url() 等引用外部文件的声明。
  • style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。
  1. 本地安装loader

    npm i -D css-loader style-loader
    
  2. 配置webpack

    module: {
            rules: [
                {
                    test: /\.css$/,    //使用正则匹配所有需要使用此loader的文件
                    use: [             //先由css-loader处理后,在交给style-loader处理
                        'style-loader',
                        {
                            loader:'css-loader',
                            options:{           //传入css-loader的参数
                                minimize:true,  //是否压缩css代码
                                module:true     //是否使用css module
                            }
                        }
                    ]
                },
            ]
        },
    
3.6.2 加载SCSS

先将SCSS转成CSS,后续处理同上。

  1. 本地安装

    npm i -D sass-loader node-sass 
    
  2. 配置webpack

    module: {
            rules: [
                {
                    test: /\.scss$/,    //使用正则匹配所有需要使用此loader的文件
                    use: ['style-loader','css-loader', 'sass-loader']  //处理顺序:sass-loader->css-loader->style-loader
                },
            ]
        },
    

3.7 加载静态资源

3.7.1 加载图片&字体

file-loader, url-loader可用于处理图片,字体等静态资源。

url-loader封装了file-loader:

  • 文件大小小于limit参数时,url-loader将会把文件转为DataURL。
  • 文件大小大于limit,url-loader会调用file-loader进行处理。
  1. 本地安装loader

    npm i -D file-loader url-loader
    
  2. 配置webpack

    module: {
        rules: [
            {
                    test: /\.(png|svg|jpg|gif)$/,
                    use: [{
                        loader: 'url-loader',
                        options: {
                            limit: 1024 * 30,         //30KB 以下的文件采用 url-loader
                            fallback: 'file-loader',  //否则采用 file-loader,默认值就是 file-loader
                            outputPath: 'images',     //图片输出路径,相对于output.path
                        }
                    }]
                },
                {
                    test: /\.(eot|ttf|woff|svg)$/,
                    use: [{
                        loader: 'url-loader',
                        options: {
                            limit: 1024 * 30,         //30KB 以下的文件采用 url-loader
                            fallback: 'file-loader',  //否则采用 file-loader,默认值就是 file-loader
                            outputPath: 'fonts',      //字体输出路径
                        }
                    }]
                },
        ]
    },
    
  3. 编译后:

    • 大于30KB的资源,用file-loader处理,复制到dist/images目录下。
    • 小于30KB的资源,和js一起打包,形成dataURL形式。
3.7.2 copy-webpack-plugin

将不需要webpack处理的静态资源(e.g. favicon),原样输出到指定目录下。

  1. 本地安装

    npm i copy-webpack-plugin -D
    
  2. 配置webpack

    const CopyWebpackPlugin = require('copy-webpack-plugin');
    
    plugins:[
        new CopyWebpackPlugin([{
            from: './src/assets/public',  // 将此目录下的文件
            to:'./public'                 // 输出到此目录,相对于output.path目录
        }])
    ]
    
  3. 编译后 src/assets/public 下的文件将原封不动的输出到 dist/public 目录下。

3.8 第三方库

通过ProvidePlugin引用某些模块作为应用运行时的变量,从而不必每次都用 require 或者 import, 是内置的插件。

  1. 安装jquery

    npm i -D jquery
    
  2. 配置webpack

    plugins:[
        new webpack.ProvidePlugin({
          $: 'jquery', 
        })
    ]
    
    
  3. 在JS文件中就可直接使用jquery,不用导入。

3.9 其他

3.9.1 clean-webpack-plugin

webpack打包的文件都放在dist文件夹下,但webpack无法追踪到哪些文件是实际项目中用到的。所以建议在每次构建前都清理下dist文件夹。

  1. 本地安装插件

    npm i -D clean-webpack-plugin
    
  2. 配置webpack

    const CleanWebpackPlugin=require('clean-webpack-plugin');
    
    new CleanWebpackPlugin(['dist'])
    
3.9.2 Webpack-merge

实际项目开发中,一般会有三份配置文件:

  • webpack.dev.js 开发环境:devserver配置,生成SourceMap
  • webpack.prod.js 生产环境:压缩代码
  • webpack.common.js 公共配置

可使用webpack-merge合并配置。

  1. 本地安装

    npm i -D webpack-merge
    
  2. 拆分webpack配置

    webpack.prod.js:
    
    const merge = require("webpack-merge");
    const common = require("./webpack.common.js");     //引入公共配置
    const CleanWebpackPlugin = require("clean-webpack-plugin");
    
    module.exports = merge(common, {                   //合并配置
            plugins: [
                new CleanWebpackPlugin(["dist"])
            ],
    });
    
  3. 修改package.json的script字段

    "build": "webpack --config webpack.prod.js",
    "start": "webpack-dev-server --config webpack.dev.js"
    
3.9.3 source map

React, ES6等经过webpack转换后,代码可读性非常差,不利于在浏览器中调试代码。可通过加载 Source Map 文件,在浏览器中调试源码。

各种source map的差异见:https://github.com/webpack/webpack/tree/master/examples/source-map

devtool: "cheap-module-eval-source-map"  //开发环境

4. 优化

4.1 模式

我们在日常的前端开发工作中,一般都会有开发&生产两套构建环境。

webpack 4.X新增用mode字段指定当前环境,并启用相应模式下的webpack内置的优化。

module.exports = {
  mode: 'production'
};
选项描述
development (开发环境)process.env.NODE_ENV =development
并启用以下插件:
NamedChunksPlugin , NamedModulesPlugin
production (生产环境)process.env.NODE_ENV =production
并启用以下插件:
FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin , UglifyJsPlugin

可通过optimization字段,手动配置或覆盖mode配置。

4.2 代码压缩

4.2.1 压缩JS文件

将mode设置成production,在此模式下optimization.minimize默认为true, webpack 会自动调用UglifyjsWebpackPlugin压缩JS代码。

若不需要压缩,可将optimization.minimize设置为false。

  1. 配置webpack.prod.js

    mode: "production"
    
    optimization: {
            // minimize:false,     //若不需要进行压缩,可使用此句禁用
            // minimizer: [         //用于指定使用的压缩插件或自定义UglifyjsWebpackPlugin插件配置,覆盖默认配置。
            //     new UglifyJsPlugin({ /* your config */ })
            // ]
    },
    
4.2.2 压缩CSS文件

3.7章节介绍了如何加载样式,但加载后的样式是写在JS文件中的。随着项目越来越大,js文件也会越来越大,所以,我们就需要对css文件进行分离并压缩。

  1. 本地安装插件

    npm i -D mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
    
  2. 配置webpack:分离css文件

    webpack.common.js:
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
      plugins: [
        new MiniCssExtractPlugin({
          filename: "[name].css",    // [name]为chunk名称
        })
      ],
      module: {
        rules: [
          {
            test: /\.(sc|c)ss$/,
            use: [
                MiniCssExtractPlugin.loader,   // style-loader改用MiniCssExtractPlugin
                'css-loader',
                'sass-loader',
            ],
          }
        ]
      }
    }
    
    注:在Webpack4上用extract-text-webpack-plugin会出错,可以安装beta版本extract-text-webpack-plugin@next。
    
  3. 配置webpack:压缩css文件

    webpack.prod.js:
    const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); //用于压缩CSS代码
    const UglifyJsPlugin = require("uglifyjs-webpack-plugin");  //用于压缩JS代码
    
    optimization: {
            minimizer: [     
                new UglifyJsPlugin({}),
                new OptimizeCSSAssetsPlugin({})  //压缩css
            ]
        },
    
4.2.3 压缩HTML文件

HtmlWebpackPlugin支持压缩输出的HTML文件。

 plugins:[
    new HtmlWebpackPlugin({
    	template:'src/index.html', 
    	minify:{                     //压缩输出
            collapseWhitespace:true   //折叠空白区域
            minifyCSS: true, 	// 压缩 HTML 中出现的 CSS 代码
        	minifyJS: true 		// 压缩 HTML 中出现的 JS 代码
    	}
    })
]
4.2.4 压缩图片
  1. 本地安装

    npm i -D image-webpack-loader
    
  2. 配置webpack

    {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
            {
                loader: 'url-loader',
                options: {
                    limit: 1024 * 30,         
                    fallback: 'file-loader',  
                    outputPath: 'images',    
                }
            },
        	'image-webpack-loader'        // 压缩图片
        ]
    },
    

4.3 Tree Sharing

Tree Shaking 可以用来剔除 JavaScript 中用不上的代码。

Tree Shaking要求:

  • 必须遵循 ES6 的模块规范(即 import 和 export)。
  • 在项目 package.json 文件中,添加一个 “sideEffects” 属性。作用于引入重导出的模块。
  • 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。
  1. 新建util.js

    export funcA from './TESTA'
    export funcB from './TESTB'
    
    TESTA.js:
    import React from 'react';
    export default function funcA() {
      return <h1>I'm funcA</h1>;
    }
    console.log("funcAfuncAfuncA")   // 副作用代码
    
    TESTB.js:
    import React from 'react';
    export default function funcB() {
      return <h1>I'm funcB</h1>;
    }
    console.log("funcBfuncBfuncB")  // 副作用代码
    
  2. 在A.js中引用util.funcA,则funcB就是用不上的代码。

    import {funcA} from '../util';
    
  3. 修改babel-loader配置。

    presets: [ 
        [
            "env",
            {modules: false}  // 关闭Babel的模块转换功能,保留原本的ES6模块化语法
        ],
        "react"
    ]
    
  4. package.json添加sideEffects字段: 将文件标记为无副作用

    "sideEffects": ["*.css","*.scss"]  //避免样式文件被删除
    
    注:此字段是webpack4新增的。
    
  5. 开启压缩,参照4.2.1

    执行编译后,A.bundle.js无funcB相关代码。

4.4 提取公共代码

提取公共代码的原理:用户第一次访问页面后,页面公共代码的文件已经被浏览器缓存起来。用户切换到其它页面时,存放公共代码的文件就不会再重新加载,而是直接从缓存中获取。 加快了其他页面的访问速度,减少了网络传输流量。

3种拆分方案浏览器的加载情况:

  • 未拆分代码:所有代码被打包到一个JS文件中,当修改了代码,浏览器就得重新加载所有代码。
  • 拆分公共代码:代码被打包成两个JS文件(公共代码 & 其余代码),修改其中一个文件的代码,浏览器只需要重新加载修改的代码文件。但第三方库的代码也属于公共代码,若修改了非第三方库的公共代码,那么浏览器也会重新加载不常变更的第三方库代码。
  • 拆分公共代码&第三方库:代码被打包成3个以上的JS文件。

mode=production时,webpack 4.X 会默认对代码进行拆分,拆分的规则是:

  • 模块被重复引用,或者是来自node_modules中的模块。
  • 文件大于30kb。
  • 在按需加载时,请求数量小于等于5。
  • 在初始化加载时,请求数量小于等于3。

注:webpack 4.x 通过optimization.splitChunks配置, webpack 4.x之前可用CommonsChunkPlugin。

默认配置:

optimization: {
    splitChunks: {
      chunks: "async", //异步加载的模块
      minSize: 30000, //模块大于30k会被抽离到公共模块
      minChunks: 1, //被引用超过1次
      maxAsyncRequests: 5, //异步模块,一次最多只能被加载5个
      maxInitialRequests: 3, //入口模块最多只能加载3个
    },
  },
}

拆分公共代码配置:

optimization: {
    splitChunks: {
      chunks: "all", // 所有的 chunks 代码公共的部分分离出来成为一个单独的文件
    },
  },
}

拆分公共代码&第三方库配置:

  • 根据所使用的技术栈,找出所有页面都需要用到的基础库。如react、react-dom 等库,把它们提取到一个单独vendor.js文件。
  • 在剔除了各个页面中被 vendor.js 包含的部分代码外,再找出所有页面都依赖的公共部分的代码提取出来放到 common.js中去。
  • 再为每个页面都生成一个单独的文件,这个文件中不再包含 vendor.js 和 common.js中包含的部分,而只包含各个页面单独需要的部分代码。
optimization: {
    splitChunks: {
            cacheGroups: {           //缓存组
                commons: {           //提取入口文件之间的公共代码
                    chunks: 'all',   //块的范围,有三个可选值:initial、async、all,默认为all
                    minChunks: 2,    //被引用次数
                    minSize: 0,      //文件大小
                    name: "common"   //拆分出来块的名字
                },
                vendor: {
                    chunks: "all",
                    test: /node_modules/,//控制哪些模块被这个缓存组匹配到
                    name: "vendor",
                    priority: 10,
                },
            },
        }
}
new HtmlWebpackPlugin({
    chunks: ['A','common','vendor'],           //引入拆分出来的chunk
    filename:'A.html',      
    template:'template.html', 
    minify:{                 
    	collapseWhitespace:true  
    }
}),

编译完成后dist目录结构如下:

│  A.bundle.js
│  A.css
│  A.html
│  B.bundle.js
│  B.css
│  B.html
│  common.bundle.js           //A和B之间的公共代码
│  vendor.bundle.js           //node_modules下的第三方依赖代码
├─images
│      230280f0ff880c8273b99fdae150dc96.png
└─public
        ico.png

4.5 优化loader配置

使用 Loader 时可以通过 test 、 include、 exclude 三个配置项来命中 Loader 要应用规则的文件。 为了尽可能少的让文件被 Loader 处理,可以通过 include && exclude 去命中/排除文件。

示例配置:

babel-loader:
exclude: path.resolve(__dirname, 'node_modules')

url-loader:
include: path.resolve(__dirname, 'src/assets')

4.6 Resolve

Webpack 在启动后会从配置的入口模块出发找出所有依赖的模块,Resolve 配置 Webpack 如何寻找模块所对应的文件。

模块引入方式:

import * as m from './index.css'   // 相对路径
import React from 'react'         // 模块名
  • 解析相对路径
    1. 查找相对当前模块的路径下是否有对应文件或文件夹
    2. 是文件则直接加载
    3. 是文件夹则继续查找文件夹下的 package.json 文件
    4. 有 package.json 文件则按照文件中 browser/module/main 字段的文件名来查找文件 (配置项:resolve.mainFields )
    5. 无 package.json 或者无 browser/module/main字段则查找 index.js 文件 (配置项:resolve.mainFiles)
  • 解析模块名
    1. 查找当前文件目录下,父级目录及以上目录下的 node_modules 文件夹,看是否有对应名称的模块 (配置项:resolve.modules)
4.6.1 resolve.modules

resolve.modules 用于配置 Webpack 去哪些目录下寻找第三方模块。

默认值是 [‘node_modules’],作用:

  • 先去当前目录下的node_modules目录下查找第三方模块。
  • 如果没找到,就去上级目录的node_modules目录下查找。
  • 还是没找到,再去上级的node_modules目录下查找。
  • 直到根目录。

通常安装的第三方模块都放在项目根目录下的 node_modules 目录下,就没有必要按照默认的方式去一层层的寻找,可以指明存放第三方模块的绝对路径,以减少寻找。

配置如下:

module.exports = {
  resolve: {
    // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
    modules: [path.resolve(__dirname, 'node_modules')]
  },
};
4.6.2 resolve.extensions

resolve.extensions 用于配置在尝试过程中用到的后缀列表,默认是:

extensions: ['.js', '.json']

如果这个列表越长,或者正确的后缀在越后面,就会造成尝试的次数越多。

建议:

  • 后缀尝试列表要尽可能的小,不要把项目中不可能存在的情况写到后缀尝试列表中。
  • 频率出现最高的文件后缀要优先放在最前面,以做到尽快的退出寻找过程。
  • 在源码中写导入语句时,要尽可能的带上后缀,从而可以避免寻找过程。例如在你确定的情况下把 require('./data') 写成 require('./data.json')。

相关 Webpack 配置如下:

module.exports = {
  resolve: {
    // 尽可能的减少后缀尝试的可能性
    extensions: ['.js'],
  },
};
4.6.3 resolve.alias

resolve.alias 配置项通过别名来把原导入路径映射成一个新的导入路径,可简化import/require路径。

resolve:{
  alias:{
    'assets': path.resolve(__dirname, 'src','assets')   //把导入语句里的 assets 关键字替换成 根目录/src/assets/
  }
}

import img from 'assets/images/1.png' ==> import img from 'xx/src/assets/images/1.png'。

css中的url不支持。

解决在webstorm中无法解析alias后的路径:

  1. 在webpack settings中指定webpack配置文件的路径。
  2. 由于我们脚手架的webpack配置中resolve.alias配置是直接写在alias字段下的,webstorm无法解析。所以需要在配置文件中配置resolve.alias字段。

4.7 Hash

在打包出来的文件名上加上文件内容的hash是目前最常见的有效使用浏览器长缓存的方法,js文件如果有内容更新,hash就会更新,浏览器请求路径变化所以更新缓存,如果js内容不变,hash不变,直接用缓存。

hash类型:

  • hash 跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值。
  • chunkhash 和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。
  • contenthash 更细致地根据内容更改,生成对应的哈希值。为css文件生成独立的hash,让css文件不受js文件的影响。

webpack配置:

output:{                
        filename:'[name].[chunkhash:8].bundle.js', 
        path: path.resolve(__dirname, 'dist') 
    },
plugins:[
        new MiniCssExtractPlugin({
            filename: "[name].[contenthash:8].css",
        }),
    ],

4.8 遗留

4.8.1 热更新 (react-hot-loader)
  1. 安装

    yarn add react-hot-loader
    
  2. webpack配置

    1.使用HotModuleReplacementPlugin插件
        plugins: [
            new webpack.HotModuleReplacementPlugin(),
        ],
    2.开启devServer的模块热替换
        devServer:{
            hot: true,                 //开启模块热替换
        },
    3.在babel-loader中使用react-hot-loader/babel插件
        plugins: ["react-hot-loader/babel"             
    
  3. 入口文件设置
    import React, { Component } from 'react';
    import { AppContainer } from 'react-hot-loader';  
    import ReactDOM from 'react-dom';
    require('./a.css');
    import Test from './Test';
    
    
    function render(RootElement) {
      ReactDOM.render(
        <AppContainer>
          <RootElement />
        </AppContainer>,
        document.getElementById('app')
      );
    }
    
    render(Test);
    
    if (module.hot) {
      module.hot.accept('./Test', () => {
        render(Test);
      });
    }
    
4.8.2 Proxy

proxy 用于配置 webpack-dev-server 将特定 URL 的请求代理到另外一台服务器上。例如:

proxy: {
    '/api/test': {
        target: "http://localhost:3000", // 将URL中带有/api/test的请求代理到本地的3000端口的服务上
        pathRewrite: { '^/api': '' }, // 把URL中path部分的api移除掉
    },
}
  1. 请求到 /api/test 会被代理到请求 http://localhost:3000/api/users。
  2. 请求到 /api/test 会被代理到请求 http://localhost:3000/users。
从零搭建React + Webpack项目:初步设置 本文是“从零搭建React + Webpack项目”系列的第一部分,详细介绍了如何初始化项目、安装核心依赖、配置Babel和Webpack、创建React组件以及添加必要的npm脚本。通过手动配置这些工具,读者不仅可以构建一个灵活可靠的开发环境,还能深入理解前端工程的内在机制。本系列后续文章将探讨如何为项目增添更多功能和优化,包括版本控制、支持Less、CSS模块化、代码格式化以及TypeScript的集成,旨在帮助读者构建一个高效且可维护的现代Web开发环境。 阅读详情

相关推荐

从零开始搭建webpack+react+redux项目

从零开始搭建webpack+react+redux项目 前段时间做了一个react项目,现在总结一下项目搭建的过程。 1. 准备 动手之前,我们首先要想好要用到的技术栈。我的项目使用了webpack4+react 16+redux+react-redux+react-router的技术栈,网络请求使用axios库,为了书写方便,css使用scss预处理器。 然后需要考虑项目打包出来最后的目录结构是...

u012510478的博客 805

使用webpack搭建react项目

1、新建webpack.base.conf.js文件,部分代码仅供参考。2、新建用于开发环境的webpack.dev.conf.js 文件。3、新建webpack.prod.conf.js文件。

叫我阿豪 2287

webpack实用配置prod--react(二)

一直以来都是使用脚手架创建应用,现在有空认真研究了一下webpack5,从零开始搭建项目,受益颇多,记下心得,以供参考。

小小•愿望的博客 392

超详细使用webpack搭建react项目

本篇文章有点长,请耐心阅读 第一步老样子还是要在文件夹里面npm init -y初始化项目 一般工具安装都是-D;-S是我们从开发到上线一直都要用到的 2、新建相关文件夹和文件 a、根目录下新建src文件夹用来存放项目文件;新建public文件夹并在里面新建index.html作为入口html文件 ​ b、根目录下新建main.js作为webpack打包的入口文件 3、安装webpack相关 (1)、安装webpack:npm i webpack -D ​ (2)、安装webpack-cli:n

weixin_43090018的博客 5855

从零开始一个webpack+react项目

从零开始一个webpack+react项目 最近在做react组件化的分享,从项目中抽离组件,那么第一步自然是搭建相关的环境 本篇旨在从零开始,用最少的配置、最少的代码、最少的依赖来搭建一个最简单的webpack+react环境 第一步,初始化项目 npm init (注意文件名符合规范) 第二步,安装webpack、webpack-cli和webpack-dev-server,...

weixin_44160385的博客 3202

【异常】前端Babel提示 Support for the experimental syntax ‘jsx‘ isn‘t currently enabled

Support for the experimental syntax 'jsx' isn't currently enabled (20:19):

奶爸的编程之路,也就一周冷个三天 4032

webpack4.XX 从零开始搭建react项目 (一)

做过好几个项目,但是项目的搭建基本都是脚手架一键生成的,或者大牛搭建好的,今天就自己从零开始搭建一个react项目,我会先打个基本框架,让基本的功能先跑起来,后面再慢慢加功能,慢慢优化,路漫漫修远兮,小伙伴们,共勉!闲言少叙,进入正题: 说明:该项目是基于window系统搭建的,ios操作方...

chunxue8373的博客 1297

Ubuntu 22.04 上 Mezzanine CMS 完整安装指南

CMS(内容管理系统)是构建博客、企业官网等静态与动态内容混合站点的核心技术栈;Django 作为 Python 生态中最成熟的 Web 框架,凭借其 ORM、Admin 和安全机制,成为 CMS 开发的首选基础。Mezzanine 是基于 Django 的轻量级 CMS,以开箱即用、零配置多语言和内置 SEO 工具为特色,特别适合中小项目快速落地。在 Ubuntu 这类强调稳定性的服务器操作系统上部署时,关键挑战并非代码逻辑,而是系统层依赖协同——如 Python 版本锁定、SQLite FTS5 支持、

weixin_33508411的博客 304

通过Webpack从零开始搭建React项目

通过Webpack从零开始搭建React项目

weixin_41796119的博客 318

ABP多租户实现

abp会调用ITenantStore.Find(Guid)方法去查tenant是否存在。要骗过abp就得自己实现ITenantStore并替换原来的实现。abp会自动解jwt里的tenant id,并且尝试验证它。这时候我们之前实现的ITenantStore就可以骗过abp。你要不想骗它也成,反正这个类是可以依赖注入的,你爱咋验证就咋验证。不用open id dict,只用jwt,让abp轻起来。在原来生成jwt的地方加入tenantId。module上也要加DependsOn。

m0_46704355的博客 471

【TestCenter】RFC2544时延测试

本文介绍了使用Spirent TestCenter测试路由器PPPOE时延的RFC2544标准方法。RFC2544定义了三种时延类型:转发时延(LILO)、存储转发时延(LIFO)和直通时延(FIFO)。文章详细说明了测试步骤,包括测试拓扑搭建、PPPOE环境配置、RFC2544测试任务创建等关键环节,并介绍了如何设置时延参数和查看测试报告。测试结果表明,大字节帧的时延高于小字节帧,且负载越高时延越大。该测试方法遵循国际标准,可全面评估网络设备在不同负载下的性能表现。

lai_2020的博客 1405

从零开始使用 webpack5.x 搭建 react 项目

搭建一个react-webpack框架 环境: webpack 5.x 疑问:明明已经有create-react-app脚手架了,为何还要自己去用webpack去重新做一个脚手架? 当然,create-react-app是一个很棒的脚手架了,我们查看其源码,知道其也是在webpack基础上封装的一个脚手架工具,再细看其配置内容,就知道为何需要自己封装一个自己的框架了。 1.因为其在打包时,是将node_module目录下所有包都打包到一个vender.chunk里的,而所有自定义的js部分,都打包到一

木槿小凡的博客 1511

从 30 天试用到企业级落地,2026 年学习和试用 SAP AI 的完整路线图

摘要:SAP AI产品体系存在试用版本与商业版本的边界模糊问题,需区分业务嵌入AI(如Joule)、开发者工具(SAP Build Code)和AI基础平台(Generative AI Hub)三个层级。建议学习路径:1)通过SAP Learning专区掌握理论框架;2)优先使用30天Generative AI Hub基础试用体验模型编排;3)结合90天BTP试用账户开发完整应用。特别注意免费计划已陆续停止,需以最新官方文档为准。开发者应重点关注AI如何与SAP技术栈(CAP、Fiori等)集成,而非单纯功

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 115

从零开始搭建一个react项目(不使用react脚手架,webpack4+、babel7、react)

1.使用 npm init 命令生成 package.json 文件 2.安装需要的依赖 npm install --save react(安装React) npm install --save react-dom (安装React Dom) npm install --save-dev webpack (安装webpack,打包工具) npm install --save-d...

web杉杉 1399

(四)激光线扫描-光平面标定

激光发射器投射出一条线,形成的一个扇形区域平面就是光平面,也叫光刀面,与物体相交就形成了一道线激光。如果物体形状不是规则的,自然相交的线就会是一条蜿蜒曲折的线条。AxByCzD0AxByCzD0从而可以获得图像线激光上像素点的深度。💡 注意:平面方程是在同一个坐标系下定义的(如相机坐标系),所以一旦激光平面标定完成之后,激光器和相机的相对位置就必须固定不动了。

FlyWine的博客 6139

(11)(2.2.2) BLHeli32,AM32, and BLHeli_S ESCs(一)

Ardupilot官网关于《Peripheral Hardware》的翻译 —《(2.2.2) BLHeli32,AM32, and BLHeli_S ESCs》。

无人机开发(开源Ardupilot) 2232

使用Webpack搭建React项目:从零开始

🤍 前端开发工程师、技术日更博主、已过CET6🍨、23年度博客之星前端领域TOP1🕠高级专题作者、打造专栏🍚签约作者、上架课程。

你若盛开,清风自来 581

【AUTOSAR OS】调度表管理模块深度解析

本文深入解析AUTOSAR OS调度表的实现机制。调度表通过预定义到期点序列控制任务激活时序,支持周期性执行、多核协同和容错处理。核心数据结构包括调度表控制块(STCB)、配置结构和到期点动作。关键函数涵盖初始化、启动、到期点处理及链表管理,采用O(n)时间复杂度。以发动机控制为例展示配置与使用方式,并探讨显式同步、时间容错等高级特性。最后提出EP数量控制、链表优化等性能建议,为汽车ECU时间触发系统开发提供参考。

1606

React JS环境搭建

使用webpack搭建React JS简单的demo webpack:http://webpack.github.io/ 1.安装react和react-dom: npm install react react-dom 2.安装babel,babel-loader npm install babel-core@5 babel-loader 3.安装webpack npm insta

Gaaidou的博客 558

20 Embedding 与向量检索:语义为什么能变成距离

Embedding 是 RAG 语义检索的数学基础。本文用工程视角讲清文本如何变成向量,为什么语义相近会表现为距离相近,Top-K 检索如何工作,以及在模型选择、同模型约束、精确匹配和召回评估上的常见坑。

老于的笔记的专栏 6204

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

从零开始手动搭建webpack+react项目

基本步骤: 1、使用 npm init 命令生成 package.json 文件 2、安装需要的依赖 安装React npm install --save react 安装React Dom npm install --save react-dom 安装webpack打包工具 npm install --save-dev webpack 使用 webpack 4+ 版本,还需要安装 webpack-cli npm install --save-dev webpack-cli

你,若盛开的专栏 438

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

从零开始使用webpack搭建一个react项目

从零开始使用webpack搭建一个react项目

qq_42750608的博客 753

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

手动搭建 ReactJS 项目

手动搭建 ReactJS 项目 创建项目 mkdir react-app cd react-app npm init 安装 Webpack 包 npm install --save-dev webpack 配置 webpack.config.js var path = require('path'); module.exports = { // 入口文件 entry: ['....

幸凉的博客 496

从零开始搭建React+TypeScript+webpack开发环境-基于lerna的webpack项目工程化改造

Lerna 是一个用于管理具有多个包(package)的 JavaScript 项目的工具。它优化了拥有多个包的代码库的工作流程,使得更容易进行本地开发、版本控制和发布。Lerna 可以将大型代码库拆分成多个包,并将这些包存储在单个代码仓库中。每个包都可以独立地进行版本管理和发布。Lerna 支持统一版本和独立版本两种模式。统一版本模式会确保所有包的版本号相同,而独立版本模式允许每个包有不同的版本号。Lerna 提供了一组命令,使得在进行本地开发时能够更轻松地进行包之间的协同工作。

羲和实验室 1590
上一篇: git命令大全及使用大全
下一篇: webpack从入门到门口
伍伍怀
博客等级 码龄8年 24粉丝 29原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值