最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webpack搭建React项目(2)

    正文概述 掘金(0²)   2021-01-09   433

    之前通过一些 webpack 的简单配置,项目可以使用 React 来编写基本组件了,但是每次编写组件,都需要重新执行webpack命令打包,然后再手动在浏览器中打开页面才能看到开发结果,这是十分影响开发效率的,我们要的效果yarn start启动项目运行,在开发过程中使用ctrl+s保存文件,然后页面能自动更新,为此需要配置 HMR 和 webpack-dev-server

    开发模式

    如果要使用开发模式的功能,需要为 webpack 指定开发模式,因为执行webpack命令的时候如果不指定模式,那么默认就是生产环境production

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

    针对不同的模式,webpack 使用相应模式的内置优化,例如开启一些内置的plugin等,具体如下:

    development

    • DefinePlugin中注册全局环境变量属性process.env.NODE_ENV = "development"

    • 启用NamedChunksPlugin

    • 启用NamedModulesPlugin

    production

    • DefinePlugin中注册全局环境变量属性process.env.NODE_ENV = "production"
    • 启用FlagDependencyUsagePlugin,用于标记当前模块中import部分使用到的import,后续可以删除无用的import
    • 启用FlagIncludedChunksPlugin,用于为每个 chunk 引入的其它 chunks 添加 id,消除无用的 chunk
    • 启用ModuleConcatenationPlugin,将所有模块连接到一个闭包函数内,可以提高代码的执行效率,这个插件是开启 tree shaking功能必须启用的插件
    • 启用NoEmitOnErrorsPlugin,在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样可以确保输出资源不会包含错误
    • 启用OccurrenceOrderPlugin,这个插件以前叫OccurenceOrderPlugin,通过模块调用次数给模块分配 ids,常用的 ids 就会分配更短的 id,使 ids 可预测,减小文件大小
    • 启用SideEffectsFlagPlugin,副作用代码标记,配合package.json"sideEffects"配置项使用,对于无副作用的代码,可以删除一些无用的export导出
    • 启用TerserPlugin,使用 terser 压缩 JS 代码

    针对不同模式的配置

    函数配置

    webpack 支持多种配置类型,一般来说简单配置的话,都可以使用module.exports导出一个 webpack 配置对象的方式来做,但是对于复杂业务情况,例如需要针对不同环境启用不同的loaderplugin等功能,这时候需要使用 webpack 的函数配置方式来解决。

    webpack 函数方式的配置可以接受两个参数:

    • 第一个参数是一个环境变量对象env,内置一些环境变量属性,webpack 命令行 环境配置 的 --env 参数,可以允许你传入任意数量的环境变量。而在 webpack.config.js 中可以访问到这些环境变量
    // 执行webpack命令,并指定生产环境,并配置环境变量NODE_ENV
    webpack --env.NODE_ENV=development --progress
    
    • 第二个参数是一个 map 对象(argv),可以从argv中获取配置项信息传递到 webpack 的配置中
    // webpack.config.js
    module.exports = function(env, argv) {
      // 接受NODE_ENV环境变量
      const NODE_ENV = env.NODE_ENV;
    
      return {
        // 判断执行命令时为webpack指定的环境模式
        mode: env.production ? 'production' : 'development',
        ...
        plugins: [
          new webpack.optimize.UglifyJsPlugin({
            // 通过argv传递参数
            compress: argv['optimize-minimize']
          })
        ]
      };
    };
    

    不同的配置文件

    如果不想都在一个webpack.config.js中杂糅所有配置项,出于维护性更高的目的,可以使用不同的 webpack 配置文件,例如开发环境指定webpack.development.config.js,并在执行webpack命令的时候通过命令行接口参数--config指定使用不同的配置文件。

    // 指定使用配置文件,config后面串接配置文件路径
    webpack --config webpack.config.js
    
    // 指定config文件夹中的webpack.development.config.js文件
    webpack --config config/webpack.development.config.js
    

    不同模式的切换

    如果直接在webpack.config.jsmode配置项中写死在 webpack 不同模式中自由的切换,一般来说有两种方式:

    • 第一种是在执行webpack命令的时候,通过 webpack 提供的命令行接口参数--mode指定模式
    // 指定开发模式
    webpack --mode=development
    
    // 指定开发模式
    webpack --mode=production
    
    • 第二种方式也是通过 webpack 提供的命令行接口参数--mode指定模式,不过结合了npm-scripts在package.json文件中配置命令,这样就能通过npm run xxx或者yarn xxx来指定 webpack 的模式了
    {
      ...
      "scripts": {
        "start": "webpack --mode=development",
        "build": "webpack --mode=production"
      },
    }
    
    • 最后一种是推荐方式,上文说过,webpack 支持函数配置方式,函数可以接受使用命令行接口参数--env传递的环境变量对象,所以这种方式是通过在 npm-scripts 中配置启动命令,传递给 webpack 配置函数的env环境对象,然后在函数内部通过env判断当前所处的模式,再写入到mode配置项中
    // package.json的scripts命令配置
    {
      ...
      "scripts": {
        "start": "webpack --env.NODE_ENV=development",
        "build": "webpack --env.NODE_ENV=production"
      },
    }
    
    module.exports = function(env){
      const isDevelopment = env.NODE_ENV === "development";
      const isProduction = env.NODE_ENV === "production";
      return {
        mode: isProduction ? "production" : isDevelopment && "development",
        ...
      }
    }
    

    WDS

    WDS,webpack-dev-server,webpack 开发服务器。根据 webpack 的介绍,webpack-dev-server 可以在本地开启一个简单的 web 服务器,并且具有 live reloading(实时重新加载) 功能,也可以使用 webpack 的观察模式来做到文件修改自动构建,但是观察模式无法在浏览器中自动刷新页面,为了看到修改后的实际效果,需要手动刷新浏览器。

    webpack-dev-server 能做到自动打包文件,并且自动刷新浏览器页面。

    首先安装 webpack-dev-server

    yarn add webpack-dev-server -D
    

    webpack.config.js中简单配置启用 webpack-dev-server

    module.exports = {
      ...
      devServer: {
        open: true,
        port: 9999,
        compress: true,
        writeToDisk: false,
      },
    }
    

    同时修改 npm-scripts 中的start配置

    {
      ...
      "scripts": {
        "start": "webpack-dev-server --env.NODE_ENV=development",
        ...
      },
    }
    

    接下来就可以执行yarn start命令查看 webpack-dev-server 的效果了。

    webpack搭建React项目(2)

    接下来测试一下修改自动刷新页面

    webpack搭建React项目(2)

    关于 webpack-dev-server 的详细配置项还有很多,需要注意的是,部分配置项带见CLI only 的表示该配置项只能用在命令行中,不能在webpack.config.js使用,见 —— 配置 - DevServer。

    配置项值类型default含义
    portnumber8000域名端口号compressbooleanfalse为每个静态文件开启 gzip 压缩httpsbooleanfalse启用 https,如果开启 https 需要自定义的证书,否则浏览器会报错openbooleanfalse告诉 dev-server 在服务器启动后在系统默认浏览器中显示页面openPagestringindex.html指定打开浏览器要浏览的页面,默认是根目录的index.htmlproxyobjectnull这个配置那是相当有用,直接用途可以不通过服务端解决开发环境的跨域请求问题,文档见 —— http-proxy-middlewarecontentBasebooleanfalse指定提供给 devServer 的静态文件的路径watchContentBasebooleanfalse监听 [devServer.contentBase]选项提供的静态文件,启用后,文件更改将触发整个页面重新加载writeToDiskbooleanfalse告诉 devServer 将自动打包的文件写入硬盘,写入的文件目录路径为webpack.config.js中配置的output.pathstatsstring采用stats 对象,控制在控制台中显示哪些信息,例如'errors-only'只在发生错误或有新的编译时输出;
    与下面的noInfoquiet一起使用时,该选项无效。
    overlayobjectfalse出现编译器错误或警告时,在浏览器中全屏显示出来noInfobooleanfalse在终端隐藏 webpack 打包过程等信息,只在出错或者警告的时候才显示这些信息quietbooleanfalse在终端隐藏 webpack 的错误或警告等信息,只在控制台显示初始启动信息liveReloadbooleantrue热重载,默认情况下,检测到文件更改时,devser 就将重新编译打包,然后刷新页面;如果启动 HMR,这个选项会被自动禁用inlinebooleantrueinline模式会自动刷新页面,并且构建消息将出现在浏览器控制台中,如果设置为false,那么页面内容将放在一个iframe里调试,并且页面顶部会出现一个调试进度条,当组件更新时,看不到页面刷新的过程hotbooleanfalse启用 HMRhotOnlybooleanfalsehotOnly将作为构建失败时的回退;如果将hotOnly设置为true,那么组件更新以后,浏览器不会自动刷新

    HMR

    webpack-dev-server 默认情况下,会开启热重载liveReload功能,检测到文件更改时,devser 就将重新编译打包,然后刷新页面;

    webpack搭建React项目(2)

    对于代码量小的项目,重新编译的时间损耗不会太多,但是项目需要使用的组件,模块等越来越多,打包过程也会越来越慢,会越来越影响开发效率。

    HMR,hot module replacement,模块热替换,是 webpack 提供的在本地开发环境的程序运行过程中,替换,添加和删除模块代码以后,无需重新加载整个页面,便能完成页面更新的功能。同时,当前页面的状态也能保存下来。

    使用 HMR,无需安装额外的插件,只需要在 devserver 的配置项中启用hot:true即可,当设置hot:true时,将自动添加HotModuleReplacementPlugin这个插件,无需其他配置。

    module.exports = {
      ...
      devServer: {
        hot: true
      },
    }
    

    成功启用 HMR 以后,浏览器的 devtool 一般会输出 HMR 的信息,例如

    webpack搭建React项目(2)

    对 React 来说,在 HMR 里做的是重新引入 root component,然后重新渲染。因为 HMR 是对 root component 的热替换,所以替换之后 root component 和它内部的 component 的 state 都会丢失,但是对于保存在像 Redux store 等外部数据容器中的状态则可以保持。

    React Fast Refresh

    社区过去一直使用React Hot Loader作为热更新 React 组件的方案,但是 19 年的时候,React 团队将 React Native 的 fast refresh 功能移植出来,支持在 web 开发中使用。

    对于React Fast Refresh,目前没有正式的介绍文档,其相关概念只存在于 GitHub 的 issue 讨论中 —— What Is Fast Refresh?

    目前,社区已经有了支持 React Fast Refresh 的 webpack 插件 —— React Refresh Webpack Plugin

    yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D
    

    使用 React Fast Refresh,需要在webpack.config.js中配置两步:

    • 启用引入 ReactRefreshWebpackPlugin;
    • babel-loader中启用react-refresh/babel这个 plugin;
    // 引入ReactRefreshWebpackPlugin
    const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
    
    module.exports = function(env){
      const isDevelopment = env.NODE_ENV === "development";
      return {
        plugins: [
          ...
          isDevelopment && new ReactRefreshWebpackPlugin(),
        ].filter(Boolean),
        module: {
          rules: [
            {
              test: /\.m?jsx?$/,
              exclude: /(node_modules)/,
              use: {
                loader: "babel-loader",
                options: {
                  ...
                  plugins: [
                    ...
                    isDevelopment && require.resolve("react-refresh/babel"),
                  ].filter(Boolean),
                },
              },
            },
          ],
        },
      }
    }
    

    这里filter(Boolean)是一个小技巧,Arrayfilter(callback)方法本身是删除掉数组中不满足条件的元素,也就是它只会把调用callback返回true的元素放入到新生成的数组中;当构造函数Boolean作为回调函数的时候,对传入Boolean只有在falseundefinedNaN0,空字符串“”的时候才会返回true

    使用&&语法的时候,那些不会在当前环境中使用的 plugin 或者 loader,它们也会返回false,有时候这种情况是会报错的,于是就使用filter(Boolean)过滤掉那些在当前环境中不使用的 plugin 或者 loader 了。

    source map

    什么是 source map

    最初的 JS 代码都会经过压缩(minify)操作,来移除代码中非必要性的空格,注释,换行符等内容,以减小代码体积。因为 JS 是嵌入在 HTML 中的,需要先通过网络获取才能解析执行,减小代码体积有利于缩短网络请求时间,进而间接地缩短网页加载的时间。

    后来又出现了代码混淆技术,通过将代码中的各种元素,如变量、函数、类的名字改写成无意义的名字,来提升 JS 在客户端的安全性。

    这些操作虽然有利于提升客户端体验,但是不利于开发人员调试,于是就出现了 source map 这项技术,将源代码和压缩后的代码对应起来,通过开启 source map 就能很好的找到映射的源代码,从而方便调试。

    source map 本质上就是一个以.map为后缀名的 JSON 文件,里面写入了一些源文件和压缩文件的映射关系属性,例如

    {
      "version": 3, //版本
      "file": "script.js.map", //source map文件名
      "sources": [
        //源文件名
        "app.js",
        "content.js",
        "widget.js"
      ],
      "sourceRoot": "/", //源文件路径
      "names": ["slideUp", "slideDown", "save"], //包含源文件中所有变量和函数名称的数组
      "mappings": "AAA0B,kBAAhBA,QAAOC,SACjBD,OAAOC,OAAO..." //
    }
    

    根据 source map 文件,在压缩后的代码文件底部通过一个注释字段sourceMappingURL写入 source map 文件的路径,告知浏览器我这个压缩文件有一个源代码文件映射可以用,例如

    //# sourceMappingURL=/path/to/script.js.map
    

    后来,在 JS 基础上衍生出来的语言,例如 JSX,TS,CoffeeScript 等也都能通过这样技术映射出来。一些浏览器也都内置了对 source map 的支持,例如 Chrome 可以在 devtool 的设置面板中开启 JS 和 CSS 的 source map。

    webpack搭建React项目(2)

    如果用了 WDS,是不需要使用 source map 的,因为开发环境的 WDS 有直观的错误提示,尤其在 React 中配合React Fast Refresh使用更加强大,可以在页面测试一下,给定以下代码:

    export default class extends Component {
      state = {
        value: '',
      };
    
      /*当输入的时候直接报错*/
      handleChange = e => {
        throw new Error('测试');
      };
    
      render() {
        return (
          <div>
            <input value={this.state.value} onChange={this.handleChange} />
          </div>
        );
      }
    }
    

    当输入出错的时候,浏览器页面立即弹出了如下的错误提示框

    webpack搭建React项目(2)

    但是,source map 在生产环境中仍然有使用的必要性,对于上面的错误测试代码,当我们执行yarn build打包代码以后,生成的目录如下:

    dist
    ├─ favicon.ico
    ├─ index.html
    └─ main.js
    

    在浏览器中打开 html 页面,输入之后立刻就会报错,从 devtool 可以获知报错信息如下:

    webpack搭建React项目(2)

    可以看到这样的报错信息指向的代码位置已经糊成一团了,如果是复杂的错误,这种代码错误定位屁用没有。

    devtool

    要在 webpack 中开启 source map,只需要一个配置项devtool,可以传入指定的模式字符串,或者使用devtool:false禁用它;如果省略devtool配置项,也就是不会生成 source map 文件。

    module.exports = {
    	...
      devtool: isProduction ? "source-map" : false,
    };
    

    推荐在生产环境下使用devtool: "source-map",执行yarn build重新打包一下,看到目录下多了一个.map文件

    dist
    ├─ favicon.ico
    ├─ index.html
    └─ main.js
    └─ main.js.map
    

    这时候打开 html 页面测试输入报错,可以看到已经成功定位到了源代码的错误点,OK!

    webpack搭建React项目(2)

    除了 webpack 自带的输出 source map 文件的功能,一些 loader 也会提供生成 source map 的配置选项,不过它们最终都依赖于devtool配置项是否启用,例如css-loader提供sourceMap的配置项,可以为 CSS 文件生成 source map。

    不过需要注意的是,生产环境如果配置devtool选项,依据不同的devtool,对构建速度的影响也不同,一般来说:

    • 指定devtool:source-map可以详细追踪到错误信息的位置,但是出现错误可以在 devtool 中直接跟踪到源码,例如上图
    • 指定devtool:eval可以显示错误位置,但是代码会是经过 babel 等编译过的代码,如果是 React 组件,大致也能分析出错误的代码位置

    webpack搭建React项目(2)

    • 指定devtool:eval-cheap-source-mapdevtool:eval看起来差不多,依旧是编译过的代码

    • 指定devtool:eval-cheap-module-source-map也能显示具体的源码位置,不过相对于devtool:source-map构建速度会大幅减少,webpack 是推荐使用这个配置项

    • 指定devtool:eval-source-mapeval-nosources-source-mapeval-nosources-cheap-source-mapeval-nosources-cheap-module-source-map也都能显示源码位置

    注意点

    如果在 webpack 的配置项optimization.minimizer中自定义terser-webpack-plugin的相关配置,哪怕只写了一个初始化terser-webpack-plugin的实例,也会对 webpack 开发环境生成的 source map 造成影响,所以必须保证开启terser-webpack-pluginsourceMap配置项,如果这个没配置,devtool也没指定,那么开发环境的代码映射就会直接映射到打包生成的 chunk 下,并不会映射到源代码。

    const TerserPlugin = require('terser-webpack-plugin'); //压缩JS代码
    
    module.exports = {
      optimization: {
        minimizer: [
          new TerserPlugin({
            sourceMap: true,
          }),
        ],
      },
    };
    

    起源地下载网 » webpack搭建React项目(2)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元