使用webpack从头搭建 react开发项目 一

1,067 阅读4分钟

初始化项目

mkdir new-webpack-react
cd new-webpack-react
npm init

安装webpack

npm install --save-dev webpack webpack-cli 

备注: npm 的各种简写的意义:

  • -i 相当于 install -s
  • -S就是--save的简写,将包的名称及版本号放在dependencies里面,dependencies 是需要发布到生产环境的
  • -D就是--save-dev 这样安装的包的名称及版本号就会存在package.json的devDependencies这个里面。 devDependencies 里面的插件只用于开发环境,不用于生产环境

配置文件webpack.config.js的配置

1.首先明确 webpack的配置文件需要的几个配置:

  • 配置入口(entry)
  • 配置出口(output)
  • 配置各种资源加载的loader
  • 插件(plugins ),解决 loader 无法实现的其他事
  • 解析器(resolve),
  • 使用HtmlWebpackPlugin将打包后的js入口文件的脚本注入到html模板中
const path = require('path'),
  htmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  /* entry的值有三种类型 : 1.字符串,2.数组, 3.对象 ,
   数组 多个入口文件时,使用数组的方式,比如依赖第三方库 bootstrap ,最终 bootstrap 会被追加到打包好的 index.js 中,数组中的最后一个会被 export
   对象  设置多个打包目标。每一对键值对都对应着一个入口文件。常用语多页面入口文件配置
   */
   
  entry: path.resolve(__dirname, '../src/index.js'), //   打包的入口文件
  output: {
    filename: '[name].index.js', //  输出文件名称,自己设置的js路径
    chunkFilename: 'chunk/[name].[hash].js', //  chunkFilename  按需加载,生成块状文件
    path: path.resolve(__dirname, '../dist'), //  输出文件位置
    publicPath: '' //  静态资源的路径前缀
  },
  plugins: [
    /* HtmlWebpackPlugin这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html */
    new htmlWebpackPlugin({
      template: path.resolve(__dirname, '../template/index.html')
      // 设置favicon的路径
      // favicon: './src/assets/favicon-32x32-next.png'
    })
  ]
}

packagejson文件配置命令并执行,可以看到dist文件夹下生成了main.923dea72093bed3467ee文件

"scripts": {
    "start": "webpack --config build/webpack.config.js"
  // "start": "webpack"
  },

备注: 如果webpack.config.js存在在根目录里, 则webpack命令将默认选择使用它.

也可以使用 webpack --config webpack.config.js 命令来指定配置文件

  1. 配置一些常用的loader,css,js等

安装 npm install --save-dev style-loader css-loader less-loader sass-loader less file-loader

module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader', // creates style nodes from JS strings
          'css-loader', // translates CSS into CommonJS
          'sass-loader' // compiles Sass to CSS, using Node Sass by default
        ]
      },
      {
        test: /\.less$/,
        use: [
          {loader: 'style-loader'},
          {loader: 'css-loader'},
          {loader: 'less-loader',
            options: { javascriptEnabled: true, modifyVars: themeVariables } // modifyVars 全局覆盖的样式,例:antd的定制全局覆盖样式
          }
        ]
      },
      {
        test: /\.css$/,
        use: [devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader']
      },
      {
        test: /\.(eot|woff|woff2|ttf|svg|png|jpe?g|gif)(\?\S*)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              // name: '[name].[ext]' //  name也可以为一个函数
              name(file) {
                if (devMode) {
                  return '[name].[hash].[ext]'
                }
                return '[name].[ext]'
              }
            },
            exclude: /node_modules/
          }
        ]
      }
    ]
  }

js 编译, 使用babel编译javascript: cnpm install --save-dev babel-core babel-loader babel-plugin-import babel-plugin-transform-class-properties babel-plugin-transform-decorators-legacy babel-plugin-transform-runtime babel-polyfill babel-preset-env babel-preset-es2015 babel-preset-stage-0 babel-preset-stage-1

编译react代码可以安装 babel-preset-react react-hot-loader

根目下新建 .babelrc文件

{
"presets": ["es2015", "react", "stage-1"],
"plugins": [
  "transform-runtime",
  "transform-decorators-legacy",
  "transform-class-properties",
  "react-hot-loader/babel",
  [
    "import",
    {
      "libraryName": "antd",
      "libraryDirectory": "es",
      "style": true
    }
  ]
]
/*  transform-runtime:  解决编译中产生的重复的工具函数,减小代码体积
 *   transform-remove-console: 编译后的代码都会移除console.,可在webpack中配置
 *  transform-decorators-legacy: 装饰器配置 @
 *  transform-class-properties: class属性转换
 *  react-hot-loader:热更新
 *  执行顺序: 先编译plugins里边的配置
 */

在webpack.config.js的rules 增加js、jsx的编译器

{
  test: /\.(js|jsx)$/,
  exclude: /node_modules/,
  loader: 'babel-loader'
}

接下来做一些插件的优化(plugins)

一、HtmlWebpackPlugin配置优化

HtmlWebpackPlugin各属性配置详解:

title:生成html文件的标题

filename:输出的html的文件名称

template:html模板所在的文件路径

inject:注入选项。有四个选项值 true, body, head, false.

  • true:默认值,script标签位于html文件的 body 底部
  • body:script标签位于html文件的 body 底部(同 true)
  • head:script 标签位于 head 标签内
  • false:不插入生成的 js 文件,只是单纯的生成一个 html 文件

minify: 对 html 文件进行压缩,minify 的属性值是一个压缩选项或者 false 。默认值为false, 不对生成的 html 文件进行压缩。

 new HtmlWebpackPlugin({
  // 打包输出HTML
    title: 'Hello World app',
    minify: {
    // 压缩HTML文件
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空白符与换行符
        minifyCSS: true // 压缩内联css
    },
    filename: 'index.html',
    template: path.resolve(__dirname, '../template/index.html')
    })

二、clean-webpack-plugin

cnpm install --save-dev clean-webpack-plugin 如果安装3.0 以上版本,引入方法和之前不一样 3.0版本之前:

const CleanWebpackPlugin = require('clean-webpack-plugin')
plugins:[
    new CleanWebpackPlugin(['dist'])
]

3.0版本之后:

const { CleanWebpackPlugin } = require('clean-webpack-plugin'),
plugins:[
     new CleanWebpackPlugin(),      //  备注:不用传需要clean的文件
]

三,happypack cnpm install happypack --save-dev

原因:运行在 Node.js 之上的 Webpack 是单线程模型的,所以Webpack 需要处理的事情需要一件一件的做,不能多件事一起做,我们需要Webpack 能同一时间处理多个任务,发挥多核 CPU 电脑的威力,HappyPack 就能让 Webpack 做到这点,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。

并且由于 JavaScript 是单线程模型,要想发挥多核CPU的能力,只能通过多进程去实现,而无法通过多线程实现。

由于HappyPack 对file-loader、url-loader支持的不友好,所以不建议对该loader使用。

修改webpack.config.js文件:

const HappyPack = require('happypack');
const os = require('os');
<!--线程数,改为电脑核数-->
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        loader: 'happypack/loader?id=happyBabel',  //把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行
        exclude: /node_modules/    //排除node_modules 目录下的文件
      },
    ]
  },
plugins: [
    new HappyPack({
        
      id: 'happyBabel',  //用id来标识 happypack处理那里类文件
      loaders: [{
        loader: 'babel-loader',  //如何处理  用法和loader 的配置一样
      }],
      threadPool: happyThreadPool,  //共享进程池
      verbose: true,  //允许 HappyPack 输出日志
    })
  ]
}

四、progress-bar-webpack-plugin 编译进度条插件

可在命令行里看到编译时的进度

const ProgressBarPlugin = require(progress-bar-webpack-plugin)
new ProgressBarPlugin({
     format: '  build [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)'
   }),

五、mini-css-extract-plugin npm install --save-dev mini-css-extract-plugin

将CSS提取为独立的文件的插件,对每个包含css的js文件都会创建一个CSS文件,支持按需加载css和sourceMap

  • 异步加载
  • 不重复编译,性能更好
  • 只针对css
const MiniCssExtractPlugin = require('mini-css-extract-plugin')


 {
        test: /\.css$/,
        use: [devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader']
},
      
new MiniCssExtractPlugin({
      filename: devMode ? '[name].[hash:4].css' : '[name].[contenthash].css',
      chunkFilename: devMode ? '[id].[hash:4].css' : '[id].[contenthash].css',
    })

六、 webpack.DefinePlugin

创建一个在编译时可以配置的全局变量,主要针对在编译时,区分开发,测试,生产环境。

因为nodejs里的环境变量process.env.NODE_ENV 只能在node环境里拿到,而 webpack.DefinePlugin提供的变量可以在浏览器环境里拿到

 new webpack.DefinePlugin({
  'gropuPcApiType': {
    ENV: JSON.stringify(process.env.ENV)
  }
})

index.js:
console.log('gropuPcApiType', gropuPcApiType)

上边代码在浏览器环境里提供了一个名为“gropuPcApiType” 的变量,可通过js 获取到定义的该变量,获取到的值为启动环境时的值:即:development 或者development

七、case-sensitive-paths-webpack-plugin

强制执行所有必须模块的整个路径,匹配磁盘上实际路径的确切大小写。即可以忽略大小写的问题,避免大小写问题引起的麻烦。有时你会发现 Mac 上 webpack 编译没有问题,但是到 linux 机器上就不行了,这是因为 Mac 系统是大小写不敏感 的,避免的办法是使用 case-sensitive-paths-webpack-plugin 模块

const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')

new CaseSensitivePathsPlugin()

八、NamedModulesPlugin

在热加载时,直接返回更新文件的文件名 而不是id,一般用于生产环境:

new webpack.HotModuleReplacementPlugin(),

解析器 resolve,见下一节:webpack 从头搭建 react 开发项目之 二