前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >webpack 4 的 30 个步骤打造优化到极致的 react 开发环境

webpack 4 的 30 个步骤打造优化到极致的 react 开发环境

作者头像
夜尽天明
发布2019-06-18 16:48:38
2.2K0
发布2019-06-18 16:48:38
举报
文章被收录于专栏:全栈修炼全栈修炼

将 react 和 webpack4 进行结合,集 webpack 的优势于一身,从 0 开始构建一个强大的 react 开发环境。

其实很多人都有 一看就会,一做就废 的特点(当然也包括我在内),这个时候,你需要制定一个略微详细的计划,就比如我这篇会首先列出知识点,列出大的方向,制定思维导图,然后根据思维导图编写代码,计划明确,就会事半功倍,因此,希望你可以跟着本篇循序渐进的跟着代码走一遍,不管是真实开发,还是面试,都有的扯

一、基础配置

1、init 项目

代码语言:javascript
复制
mkdir react-webpack4-cook
cd react-webpack4-cook
mkdir src
mkdir dist
npm init -y

2、安装 webpack

代码语言:javascript
复制
yarn add webpack webpack-cli webpack-dev-server -D 

touch webpack.config.js

代码语言:javascript
复制
module.exports = {
    mode: "development",
    entry: ["./src/index.js"],
    output: {
        
        path: path.join(__dirname, "dist"),
        
        filename: "bundle.js"
    },
    module:{},
    plugins:[],
    devServer:{}

}

3、安装 react 并编写代码

这部分代码篇幅过多,就是一些简单的 react 和 react-router 的一些代码编写,可以去 github 上查看,这里只阐述基本功能

代码语言:javascript
复制
cd src 
cnpm i react react-router-dom -S
代码语言:javascript
复制
|-src
│      index.js 主文件
├───pages
│      Count.jsx -- 实现了一个计数器的功能,点击按钮,会让数字增加,按钮会实时显示在页面上
│      Home.jsx -- 一个简单的文字展示
└───router
       index.js -- 路由配置文件,两个页面分别对应两个路由 count和 home

4、babel 编译 ES6、 JSX 等

代码语言:javascript
复制
cnpm i babel-loader @babel/core @babel/preset-env  @babel/plugin-transform-runtime   @babel/preset-react -D

cnpm i @babel/polyfill @babel/runtime
代码语言:javascript
复制
 {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "babel-loader"
          }
        ]
      }

新建.babelrc文件

代码语言:javascript
复制
{
  "presets": ["@babel/preset-env","@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime"]
}

5、按需引入polyfill

在 src 下的 index.js 中全局引入 @babel/polyfill 并写入 ES6 语法 ,但是这样有一个缺点:

  1. 全局引入 @babel/polyfill 的这种方式可能会导入代码中不需要的 polyfill,从而使打包体积更大

更改 .babelrc,只转译我们使用到的

代码语言:javascript
复制
npm install core-js@2 @babel/runtime-corejs2 -S 
代码语言:javascript
复制
{
  "presets": ["@babel/preset-env",
              { "useBuiltIns": "usage" },
              "@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime"]
}
代码语言:javascript
复制
将全局引入这段代码注释掉

这就配置好了按需引入。配置了按需引入 polyfill 后,用到 es6 以上的函数,babel 会自动导入相关的 polyfill,这样能大大减少 打包编译后的体积。

5、插件 CleanWebpackPlugin

你经过多次打包后会发现,每次打包都会在 dist 目录下边生成一堆文件,但是上一次的打包的文件还在,我们需要每次打包时清除 dist 目录下旧版本文件

代码语言:javascript
复制
cnpm install  clean-webpack-plugin -D
代码语言:javascript
复制


const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 
plugins: [
    new CleanWebpackPlugin() 

]

6、使用插件 HtmlWebpackPlugin

经过上一步的操作,index.html 也被清除了。因此我们将使用 HtmlWebpackPlugin 插件,来生成 html, 并将每次打包的 js 自动插入到你的 index.html 里面去,而且它还可以基于你的某个 html 模板来创建最终的 index.html,也就是说可以指定模板哦。

代码语言:javascript
复制
cnpm install html-webpack-plugin -D
cd src
touch template.html
代码语言:javascript
复制
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>react-webpack4-cook</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
 
// webpack.config.js做出更改
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      filename: 'index.html', // 最终创建的文件名
      template: path.join(__dirname, 'src/template.html') // 指定模板路径
    })
  ]

7、使用 source-map,对 devtool 进行优化

webpack 中 devtool 选项用来控制是否生成,以及如何生成 source map。简言之,source map 就是帮助我们定位到错误信息位置的文件。正确的配置source map,能够提高开发效率,更快的定位到错误位置。

在 webpack.config.js 中选项 mode 下加上如下这句话:

代码语言:javascript
复制
devtool:"cheap-module-eval-source-map",
devtool:"cheap-module-source-map", 

8、使用 WebpackDevServer

webpack-dev-server 就是在本地为搭建了一个小型的静态文件服务器,有实时重加载的功能,为将打包生成的资源提供了web服务

代码语言:javascript
复制
  devServer: {
    hot: true,
    contentBase: path.join(__dirname, "./dist"),
    host: "0.0.0.0", 
    port: 8080,
    historyApiFallback: true, 
    proxy: {
      "/api": "http://localhost:3000"
    }
  }

9、使用 HotModuleReplacement (热模块替换HMR)

建立了开发环境本地服务器 后,当修改内容后,网页会同步刷新,我们现在进入 toCount 页面

  1. 点击按钮,将数字加到一个不为 0 的数,比如加到 6
  2. 然后你可以在代码中改变按钮的文字,随便改点东西,会发现,页面刷新后,数字重新变为 0

这显然不是我们想要的,想要的是,能不能把页面的状态保存了,也就是更改了代码后,页面还是保存了数字为 6 的状态,也就是实现局部更改,首先需要用到:HotModuleReplacementPlugin 插件

代码语言:javascript
复制
devServer: {
    hot: true
},
plugins: [
    new webpack.HotModuleReplacementPlugin()
],

完事之后,继续更上边的操作,点击按钮,数字增加,然后更改内容,发现还是没有保存状态。。。what?怎么办

对@!这还没完呢,接着往下看,我们还需要 react-hot-loader 这个插件

10、react-hot-loader 记录 react 页面留存状态 state

我们继续接着上边的进行操作,分一下四步

代码语言:javascript
复制
cnpm i react-hot-loader -D
代码语言:javascript
复制
import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";-------------------1、首先引入AppContainre
import { BrowserRouter } from "react-router-dom";
import Router from "./router";
代码语言:javascript
复制
renderWithHotReload(Router);-------------------2、初始化
代码语言:javascript
复制
if (module.hot) {-------------------3、热更新操作
  module.hot.accept("./router/index.js", () => {
    const Router = require("./router/index.js").default;
    renderWithHotReload(Router);
  });
} 
function renderWithHotReload(Router) {-------------------4、定义渲染函数
  ReactDOM.render(
    <AppContainer>
      <BrowserRouter>
        <Router />
      </BrowserRouter>
    </AppContainer>,
    document.getElementById("app")
  );
}

好了,现在你再试试

11、编译css和scss

代码语言:javascript
复制
cnpm install css-loader style-loader sass-loader node-sass -D

代码语言:javascript
复制
{
  test: /\.scss$/,
    use: [
      "style-loader", 
      "css-loader", 
      "sass-loader" 
    ]
}

12、集成 postcss

最关心的还是这有啥用啊?自动增加前缀, postcss-cssnext 允许你使用未来的 css 特性,并做一些兼容处理。

代码语言:javascript
复制
cnpm install  postcss-loader postcss-cssnext -D
代码语言:javascript
复制
{
    test: /\.scss$/,
        use: [
            "style-loader", 
            "css-loader", 
            "postcss-loader",
            "sass-loader" 
        ]
}

通过控制台查看,已经自动加上了前缀

13、处理图片

代码语言:javascript
复制
cnpm i file-loader url-loader -D
代码语言:javascript
复制
 
// file-loader 解决css等文件中引入图片路径的问题
// url-loader 当图片较小的时候会把图片BASE64编码,大于limit参数的时候还是使用file-loader 进行拷贝
{
    test: /\.(png|jpg|jpeg|gif|svg)/,
    use: {
      loader: 'url-loader',
      options: {
        outputPath: 'images/', 
        limit: 10 * 1024
      }
    }
}

14、处理字体

代码语言:javascript
复制
{
        test: /\.(eot|woff2?|ttf|svg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name]-[hash:5].min.[ext]',
              limit: 5000, 
              publicPath: 'fonts/',
              outputPath: 'fonts/'
            }
          }
        ]
      }

二、webpack优化

1、alias 对文件路径优化

a. extension: 指定 extension 之后可以不用在 require 或是 import 的时候加文件扩展名,会依次尝试添加扩展名进行匹配。

b. alias: 配置别名可以加快webpack查找模块的速度。

代码语言:javascript
复制
  resolve: {
    extension: ["", ".js", ".jsx"],
    alias: {
      "@": path.join(__dirname, "src"),
      pages: path.join(__dirname, "src/pages"),
      router: path.join(__dirname, "src/router")
    }

  },

c. 使用静态资源路径 publicPath (CDN)

CDN 通过将资源部署到世界各地,使得用户可以就近访问资源,加快访问速度。要接入 CDN,需要把网页的静态资源上传到 CDN 服务上,在访问这些资源时,使用 CDN 服务提供的URL。

代码语言:javascript
复制
output:{
 publicPatch: '//【cdn】.com', 
}

2、MiniCssExtractPlugin ,抽取 css 文件

如果不做配置,我们的 css 是直接打包进 js 里面的,我们希望能单独生成 css 文件。 因为单独生成 css,css 可以和 js 并行下载,提高页面加载效率。

代码语言:javascript
复制
cnpm install mini-css-extract-plugin -D
代码语言:javascript
复制
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

 {
        test: /\.scss$/,
        use: [
          
          MiniCssExtractPlugin.loader,
          "css-loader", 
          "postcss-loader",
          "sass-loader" 
        ]
      },
 plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ]

3、代码分割按需加载、提取公共代码

为什么要实现按需加载?

我们现在看到,打包完后,所有页面只生成了一个bundle.js,当我们首屏加载的时候,就会很慢。因为他也下载了别的页面的 js,也就是说,执行完毕之前,页面是 完!全!空!白!的!。 如果每个页面单独打包自己的 js,就可以在进入页面时候再加载自己的 js,首屏加载就可以快很多。

代码语言:javascript
复制
  optimization: {
    splitChunks: {
      chunks: "all", 
    },
  },

5、文件压缩

webpack4 只要在生产模式下, 代码就会自动压缩

代码语言:javascript
复制
mode:productioin

6、暴露全局变量

可以直接在全局使用 $ 变量

代码语言:javascript
复制
 new webpack.ProvidePlugin({
      $: 'jquery', 
      jQuery: 'jQuery' 
    })

8、指定环境,定义环境变量

代码语言:javascript
复制
plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        VUEP_BASE_URL: JSON.stringify('http://localhost:9000')
      }
    }),
]

9、css Tree Shaking

代码语言:javascript
复制
npm i glob-all purify-css purifycss-webpack --save-dev
代码语言:javascript
复制
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins:[
    
    new PurifyCSS({
      paths: glob.sync([
        
        path.resolve(__dirname, './src/*.html'), 
        path.resolve(__dirname, './src/*.js')
      ])
    })
]

10、js Tree Shaking

清除到代码中无用的js代码,只支持 import 方式引入,不支持 commonjs 的方式引入。

只要 mode 是 production 就会生效,develpoment 的 tree shaking 是不生效的,因为 webpack 为了方便你的调试。

代码语言:javascript
复制
  optimization: {
    usedExports:true,
  }

11、DllPlugin 插件打包第三方类库

项目中引入了很多第三方库,这些库在很长的一段时间内,基本不会更新,打包的时候分开打包来提升打包速度,而 DllPlugin 动态链接库插件。

其原理就是把网页依赖的基础模块抽离出来打包到 dll 文件中,当需要导入的模块存在于某个 dll 中时,这个模块不再被打包,而是去 dll 中获取。

安装 jquery,并在入口文件引入。新建 webpack.dll.config.js 文件

代码语言:javascript
复制
const path = require('path')
const webpack = require('webpack')
const src = path.resolve(process.cwd(), 'src'); 
const evn = process.env.NODE_ENV == "production" ? "production" : "development";

module.exports = {
    mode: 'production',
    entry: {
        jquery: ['jquery']
    },
    output: {
        path: path.resolve(__dirname, '..', 'dll'),
        filename: '[name].dll.js',
        library: '[name]_[hash]',
        libraryTarget: 'this'
    },
    plugins: [
        new webpack.DllPlugin({
            context: process.cwd(),
            path: path.resolve(__dirname, '..', 'dll/[name]-manifest.json'),
            name: '[name]_[hash]'
        })
    ]
}

在package.json中添加

代码语言:javascript
复制
"build:dll": "webpack --config ./build/webpack.dll.config.js",

运行

代码语言:javascript
复制
npm run build:dll

你会发现多了一个 dll 文件夹,里边有 dll.js 文件,这样我们就把我们的 jquery 这些已经单独打包了,接下来怎么使用呢?

需要再安装一个依赖

代码语言:javascript
复制
npm i add-asset-html-webpack-plugin

它会将我们打包后的 dll.js 文件注入到我们生成的 index.html 中.在 webpack.base.config.js 文件中进行更改。

代码语言:javascript
复制
代码语言:javascript
复制
 new AddAssetHtmlWebpackPlugin({
 filepath: path.resolve(__dirname, '../dll/jquery.dll.js') // 对应的 dll 文件路径
 }),
 new webpack.DllReferencePlugin({
 manifest: path.resolve(__dirname, '..', 'dll/jquery-manifest.json')
 })

好了,你可有把 new webpack.DllReferencePlugin 这个插件注释掉,打包试下,在放开打包试一下,我测试结果,注释前 5689,注释后,5302ms,才差了300ms ? 注意,我这里只有一个 jquery 包作为演示,要是你把很多个都抽离了出来呢???那岂不是很恐怖了。如果你看的有点迷迷糊糊,那推荐去线上看一下我的代码吧,一看便知。

12、使用 happypack 并发执行任务

运行在 Node 之上的 Webpack 是单线程模型的,也就是说 Webpack 需要一个一个地处理任务,不能同时处理多个任务。 Happy Pack 就能让 Webpack 做到这一点,它将任务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程。

代码语言:javascript
复制
cnpm i -D happypack
代码语言:javascript
复制
 rules: [
     {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: [
          {
            
            loader: "happypack/loader?id=busongBabel"
          }
        ]
      }
      ]
plugins:[
      new HappyPack({
      id:'busongBabel',
      loaders:['babel-loader?cacheDirectory'],
      threadPool: HappyPackThreadPool,
  })
]

13、PWA 优化策略

简言之:在你第一次访问一个网站的时候,如果成功,做一个缓存,当服务器挂了之后,你依然能够访问这个网页 ,这就是 PWA。那相信你也已经知道了,这个只需要在生产环境,才需要做 PWA 的处理,以防不测。

代码语言:javascript
复制
 cnpm i workbox-webpack-plugin -D
代码语言:javascript
复制
const WorkboxPlugin = require('workbox-webpack-plugin') 
const prodConfig = {
  plugins: [
    
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true
    })
  ]
}
代码语言:javascript
复制
在入口文件加上
代码语言:javascript
复制
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(registration => {
        console.log('service-worker registed')
      })
      .catch(error => {
        console.log('service-worker registed error')
      })
  })
}

配置完后,你可以打包到 dist 目录下,在 dist 目录下启动一个静态服务器,访问首页,然后关闭这个服务器,你会惊讶的发现:网站竟然还能够访问,哈哈,是不是很神奇?

15、合并提取 webpack 公共配置

开发环境与生产环境以及webpack配置文件的分离,具体需要用到webpack-merge,用来 合并 webpack配置。

16、最终分离配置文件(打完收工)

由于时间和篇幅的限制,基本到这里就结束了。以上,不管是提到的未提到的,或者还有一些细枝末节,github 上的源码基本都已经全部包括在内了,如果有需要可以去 github 参照配置文件,自己跟着配一份出来,会更加事半功倍

本篇所有代码线上代码 react-webpack4-cook,翻译过来叫:webpack4 和 react 的乱炖,可以跟着代码进行配置,之前有很多坑,线上代码都已经被解决了。

代码语言:javascript
复制
愿世间再无 webpack 配置工程师

如果对您有帮助,不妨给个 star,点赞关注不迷路。

作者:张不怂 https://juejin.im/post/5cea1e1ae51d4510664d1652

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈修炼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、init 项目
  • 2、安装 webpack
  • 3、安装 react 并编写代码
  • 4、babel 编译 ES6、 JSX 等
  • 5、按需引入polyfill
  • 5、插件 CleanWebpackPlugin
  • 6、使用插件 HtmlWebpackPlugin
  • 7、使用 source-map,对 devtool 进行优化
  • 8、使用 WebpackDevServer
  • 9、使用 HotModuleReplacement (热模块替换HMR)
  • 10、react-hot-loader 记录 react 页面留存状态 state
  • 11、编译css和scss
  • 12、集成 postcss
  • 13、处理图片
  • 14、处理字体
  • 二、webpack优化
    • 1、alias 对文件路径优化
      • 2、MiniCssExtractPlugin ,抽取 css 文件
        • 3、代码分割按需加载、提取公共代码
          • 5、文件压缩
            • 6、暴露全局变量
              • 8、指定环境,定义环境变量
                • 9、css Tree Shaking
                  • 10、js Tree Shaking
                    • 11、DllPlugin 插件打包第三方类库
                      • 12、使用 happypack 并发执行任务
                        • 13、PWA 优化策略
                          • 15、合并提取 webpack 公共配置
                            • 16、最终分离配置文件(打完收工)
                            相关产品与服务
                            内容分发网络 CDN
                            内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档