webpack5.0 + React17.0从零开始构建项目(持续更新中)

准备工作(完整代码)

前端开发node环境是必不可少的,如果你还没安装node环境,请先安装node,最新稳定版本。安装教程自行百度(PS: 一搜一大把)

两大必备利器:

  • webpack:  本质上,是一个用于现代 JavaScript 应用程序的_静态模块打包工具_。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle
  • babel:  是一个javaScript编译器。是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

npm install依赖包安装规则:

npm i xxx --save    <===>    npm i xxx -S    用于安装  项目(运行时,发布到生成环境时)所用到的依赖。会安装到 package.json文件的 dependencies下

npm i xxx --save-dev    <===>     npm i xxx -D   用于安装 项目构建(开发时,打包时)所用到的依赖。会安装到 package.json文件的 devDependencies下

npm run dev    <===>   npm dev (是在package.json文件的scripts下配置的执行命令)。

设置 NODE_DEV时需注意 window和mac有所不同:

window     set NODE_ENV=production && xxxx

 mac         NODE_ENV=production 空格 xxxx

初始化

  1. mkdir 创建项目目录(ps: 名字自己随便起)
  2. 执行 cd xxx 进入该项目目录,然后执行 npm init -y 简单粗暴的生成初始的package.json文件
  3. 执行  npm i webpack webpack-cli -D 
  4. 执行 npm i react react-dom -S
  5. 执行 npm i @babel/core @babel/cli @babel/preset-env  @babel/preset-react  -D  安装核心babel,以及es6和reactjsx语法的介些
  6. 执行 npm i @babel/polyfill  用于补全目标环境中缺失的特性
  7. 在根目录下创建 .babelrc文件并配置初始的es6和jsx语法解析
    {
        "presets": [
            "@babel/preset-env",
            "@babel/preset-react"
        ]
    }

     

  8. 在根目录下创建build文件夹,在其下创建 webpack.base.js   webpack.dev.js   webpack.prod.js用于配置webpack

  9. 根目录下创建src目录,用于存放源代码

  10. 如果要上传至自己的github等远程仓库的话,别忘了配置 .gitignore文件

配置webpack

webpack常用的几个配置项:

entry:入口,可为字符串和对象,分别代码单入口和多入口。

output: 出口,配置打包文件输出的路径和名称等。

mode:环境,可选值有 development   production  none。

module: 模块,其选项决定了如何处理项目中的不同类型的模块。其中rules选项用于配置各种各样的loader。

resolve:解析,配置模块如何解析;其中选项alias用来创建 import 或 require 的别名,来确保模块引入变得更简单。extensions选项用于配置按照一定顺序解析文件后缀名。

plugins:插件,用于增强webpack的构建能力,通常是处理一些loader无法完成的工作。

optimization:优化,会根据mode的值来执行不同的优化,当然你也可以自己手动配置和重写。

devServer:开发服务器,用于配置快速的开发应用程序。

devtool:用于控制是否生成,以及如何生成 source map。

配置流程

  • 配置入口:通用的入口配置方法,使用glob实现。同时使用HtmlWebpackPlugin插件自动生成html文件,将打包生成的bundle插入到body的script标签中。
const path = require('path')
const glob = require('glob');
// 自动生成html
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 配置通用的多页面应用入口,以及自动生成相应的html页面
// process.cwd()方法会返回 Node.js 进程的当前工作目录
const projectRoot = process.cwd()
const configEntry = () => {
    const entry = {}
    const htmlWebpackPlugins = []
    const entryFiles = glob.sync(path.join(projectRoot, 'src/*/index.js'))
    Object.keys(entryFiles).forEach((index) => {
        // 配置入口
        const entryFile = entryFiles[index]
        const match = entryFile.match(/src\/(.*)\/index\.js/);
        const pageName = match && match[1];
        entry[pageName] = entryFile;
        // 配置对应的自动生成html
        htmlWebpackPlugins.push(
            new HtmlWebpackPlugin({
                // 使用自定义模版, 需要在对应的目录下先创建自定义的template.html文件,否则会打包报错
                template: path.join(projectRoot, `src/${pageName}/template.html`),
                // 会将默认我们自定义的模版插入打包的css和js,重命名为index.html 并将其放到对应的出口目录下
                filename: `${pageName}/index.html`,
                chunks: [pageName],
                inject: true,
                minify: {
                    html5: true,
                    collapseWhitespace: true,
                    preserveLineBreaks: false,
                    minifyCSS: true,
                    minifyJS: true,
                    removeComments: false,
                },
            })
        )
    })
    return {
        entry,
        htmlWebpackPlugins
    }
}

const {entry, htmlWebpackPlugins} = configEntry()
  • 配置出口:
output: {
        // 输出文件的路径
        path: path.join(projectRoot, 'dist'),
        // 输出的文件名
        filename: '[name]_[chunkhash:8].js'
    },
  • 解析less,同时配置自动添加浏览器前缀,并将css抽取成单独的文件

npm i postcss-loader postcss postcss-preset-env -D   执行此命令添加 自动配置浏览器前缀的依赖。

npm i mini-css-extract-plugin -D   执行此命令,安装依赖 用来抽取css文件。

配置代码如下:

// 抽取css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    module: {
        rules: [{
            test: /.less$/,
            use: [
                MiniCssExtractPlugin.loader,
                'css-loader',
                {
                    loader: 'postcss-loader',
                    options: {
                        postcssOptions: {
                            plugins: [
                                ["postcss-preset-env"]
                            ]
                        },
                    }
                },
                'less-loader', 
            ]
        
        }]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css',
        }),
    ],

}
  • 解析图片
{
            test: /\.(png|jpg|jpeg|gif)$/i,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: false,
                    name: '[name]_[contenthash:8].[ext]',
                    outputPath: './assets/img'
                }
            }]
}
  • 解析字体
{
            test: /\.(woff|woff2|eot|otf)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: '[name]_[hash:8].[ext]',
                    outputPath: './assets/fonts'
                }
            }]
}
  • 配置optimization(生产环境) 
optimization: {
        // 提取公共模块 包括第三方库和自定义工具库
        splitChunks: {
            minSize: 0,
            chunks: "all",  // async表示抽取异步模块,all表示对所有模块生效,initial表示对同步模块生效
            cacheGroups: {
                // styles: {
                //     name: 'prod',
                //     test: /\.(css|less|scss)/,
                //     chunks: 'all',
                //     enforce: true,
                //     // 表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
                //     reuseExistingChunk: true
                // },
                commons: {
                    name: 'commons',
                    chunks: 'initial',
                    minChunks: 2,
                    reuseExistingChunk: true
                },
                vendors: {  // 抽离第三方库
                    name: 'vendors',
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10,
                    reuseExistingChunk: true
                },
                utilCommon: { // 抽离自定义工具库
                    name: "common",
                    minSize: 0,     // 将引用模块分离成新代码文件的最小体积
                    minChunks: 2,   // 表示将引用模块如不同文件引用了多少次,才能分离生成新chunk
                    priority: -20
                }
            }
        },
        runtimeChunk: true,
        minimize: true,
        minimizer: [
            new TerserPlugin({
                // 开启/禁用多进程并发运行功能 并发运行的默认数量:os.cpus().length - 1,
                // 可以直接跟数字,进行并发运行数量的设置
                parallel: true,
                terserOptions: {
                    ecma: undefined,
                    warnings: false,
                    parse: {},
                    compress: {
                        // 删除所有的 `console` 语句,可以兼容ie浏览器
                        drop_console: true,
                        pure_funcs: ['console.log', 'debugger'] // 移除console
                    },
                    output: {
                        // 最紧凑的输出
                        beautify: false,
                        // 删除所有的注释
                        comments: false,
                    }
                }
            }),
        ]
 },
  • 添加eslint检测

1. 在根目录下创建.eslintrc.js文件,在文件内可配置如下:

module.exports = {
    "parser": "babel-eslint",
    "extends": "airbnb-base",
    "env": {
        "browser": true,
        "node": true
    }
}

2.安装eslint相关依赖,npm run i eslint babel-eslint eslint-config-airbnb-base -D

  • 添加git commit提交规范检测

git提交规范检测要借助 husky实现,安装依赖 npm i husky @commitlint/config-conventional @commitlint/cli -D 

在 package.json文件中添加如下代码:

"husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }

版本相关:

软件版本通常由三位组成,形如:X.Y.Z

我们在通常发布版本时为了避免出现循环依赖,减少依赖冲突,需遵循git官方提倡的semver规范。

语义化版本规范格式:

主版本号:当你做了不兼容的API的修改;

次版本号:当你做了向下兼容的功能性的增强;

修订号:当你做了向下兼容的bug修复;

构建分析:

1. 可以使用webpack内置的stats进行分析,stats构建的统计信息。在package.json文件中使用stats

"scripts": {
    "build:stats": "webpack --config build/webpack.prod.js --json > stats.json",

  },
// 此命令执行完毕后会在根目录输出stats.json文件,文件内包含了打包的统计信息

2. 在nodejs中使用

const webpack = require('webpack')
const config = require('./build/webpack.prod.js')('production')

webpack(config, (err, stats) => {
    if(err){
        return console.error(err)
    }
    if(stats.hasErrors){
        return console.error(stats.toString('errors-only'))
    }
    console.log(stats)
})

上述两种方法  颗粒度太粗,看不出问题所在。

打包体积分析

借助于webpack-bundle-analyzer插件来分析打包的体积,从而发现大的打包文件,进行针对性的优化。

 执行 npm install webpack-bundle-analyzer --save-dev,代码如下:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins: [
        // 开启 BundleAnalyzerPlugin 
        new BundleAnalyzerPlugin()
]

提升构建速度(多进程打包)

使用 thread-loader,首先执行npm i thread-loader -D  使用方式参考webpack官网, 移步这里

预编译资源模块(使用dllPlugin) 

使用缓存,开启babel-loader的缓存,使用TerserPlugin(webpack5.0自带了该插件,已经进行了一些优化的默认配置),使用cache-loader进行模块的缓存。

缩小构建目标范围:1.优化resolve.moduled配置来减少模块搜索层级。2. 优化resolve.mainFields配置。 3. 优化resolve.extensions配置。4.合理使用alias

图片压缩处理:imagemin.    使用image-webpack-loader

构建体积优化:使用动态Polyfill。  polifill-service其原理是识别User Agent 下发不同的Polyfill https://polyfill.io/v3/

 

你可能感兴趣的:(前端,Webpack,javascript,webpack)