npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

kgm-webpack-builder

v1.0.2

Published

webpack构建通用配置包

Downloads

1

Readme

kgm-webpack-builder

介绍

webpack构建通用配置包

软件架构

软件架构说明

安装教程

  1. xxxx
  2. xxxx
  3. xxxx

使用说明

  1. xxxx
  2. xxxx
  3. xxxx

参与贡献

  1. Fork 本仓库
  2. 新建 Feat_xxx 分支
  3. 提交代码
  4. 新建 Pull Request

构建配置包说明

构建配置包设计

  1. 通过多个配置文件管理不同环境的webpack配置
    • 基础配置: webpack.base.js
    • 开发环境: webpack.dev.js
    • 生产环境: webpack.prod.js
    • SSR环境: webpack.ssr.js
  2. 抽离成一个npm包统一管理
    • 规范:Git日志、readme、ESLink、Semer规范
    • 质量:冒烟测试、单元测试、测试覆盖率和CI
  3. 通过webpack-merge组合配置
    merge = require('webpack-merge)
    merge(
        {a: [1], b: 5, c: 20},
        {a: [2], b: 3, d: 20},
    )
    => {a: [1, 2], b: 3, c: 20, d: 20}
    // 合并配置
    module.exports = merge(baseConfig, devConfig)

功能模块设计

基础配置:webpack.base.js

  1. 资源解析
    • 解析ES6
      • npm i @babel/core @babel/preset-env babel-loader -D // 用来编译ES6
      • npm i -D @babel/plugin-proposal-class-properties // 是用来编译class类的
    • 解析React
      • npm i -S react react-dom @babel/preset-react // 用来编译react
    • 解析CSS
      • npm i -D css-loader style-loader // 解析css
    • 解析Less
      • npm i -D less less-loader // 解析less
    • 解析图片
      • npm i -D file-loader
      • npm i -D url-loader // 也可以处理字体的图片,可以设置较小资源自动base64
    • 解析字体
  2. 样式增强
    • css前缀补齐
      • npm i postcss-loader autoprefixer -D
    • css px转rem
      • npm i px2rem-loader -D
      • npm i lib-flexible -S
  3. 目录清理
    • npm i clean-webpack-plugin -D
  4. 多页面打包
    • npm i -D glob
  5. 命令行信息显示优化
    • npm i -D friendly-errors-webpack-plugin
  6. 错误捕获和处理
  7. css提取成一个单独的文件

开发环境:webpack.dev.js

  1. 代码热更新
    • css热更新
    • js热更新
  2. sourcemap

生产环境: webpack.prod.js

  1. 代码压缩
  2. 文件指纹
  3. Tree Shaking
  4. Scope Hositing
  5. 速度优化: 基础包CDN
    • npm i -D html-webpack-externals-plugin
  6. 体积优化: 代码分隔
    • npm install --save-dev @babel/plugin-syntax-dynamic-import
    • 修改文件:.babelrc
      { "plugins": [ "@babel/plugin-syntax-dynamic-import" ] }

SSR环境: webpack.ssr.js

  1. output 的 libraryTarget 设置
  2. css 解析 ignore

tools环境:webpack.tiils.js

  1. 用于针对性打包的配置
    • npm i terser-webpack-plugin -S

目录结构设计

  1. 结构如下:(test:放置测试代码, lib:放置源代码)
    /test
    /lib
        - webpack.base.js
        - webpack.dev.js
        - webpack.prod.js
        - webpack.ssr.js
    README.md
    CHANGELOG.md
    .eslinrc.js
    package.json
    index.js

配置开始

完善文件内容

  1. 项目初始化:npm init -y
  2. 创建文件夹/文件
    • webpack.base.js
    • webpack.dev.js
    • webpack.prod.js
    • webpack.ssr.js
  3. 安装插件:npm i webpack-merge -D

使用ESLint规范构建脚本

  1. 使用 eslint-config-airbnb-base
    • npm i eslint babel-eslint eslint-config-airbnb-base -D
    • eslint --fix 可以自动处理空格
  2. 创建文件: .eslintrc.js
    module.exports = {
        "parser": "babel-eslint",
        "extends": "airbnb-base",
        "env": {
            "browser": true,
            "node": true
        },
        "rules": {
            <!-- "global-require": 0 ,
            "no-console":"off"
            "semi": 0,              // 结尾不加分号
            'generator-star-spacing': 'off',
            "no-tabs":"off",
            "indent": "off", // h忽略换行, tab时4个空格才对["error", 4]
            "linebreak-style": ["off", "windows"],  // 回车和换行符问题
            "no-console": "off",        // 允许console.log
            "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], // js文件中可以写jsx语法 -->
        }
    }
  3. 修改文件:package.json
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "eslint": "eslint ./lib --fix"
    }
  4. 运行检查eslint:npm run eslint

package.json文件

- 要注意依赖和非依赖的位置
```
{
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "eslint": "eslint ./lib --fix",
        "dev": "webpack-dev-server --config ./lib/webpack.dev.js --open",
        "build": "webpack --config ./lib/webpack.prod.js"
    },
    "devDependencies": {
        "eslint": "^6.8.0",
        "eslint-config-airbnb": "^18.0.1",
        "eslint-config-airbnb-base": "^14.0.0",
        "eslint-loader": "^3.0.3",
        "eslint-plugin-import": "^2.20.1",
        "eslint-plugin-jsx-a11y": "^6.2.3",
        "eslint-plugin-react": "^7.18.3",
        "babel-eslint": "^10.1.0"
    },
    "dependencies": {
        "webpack": "^4.41.6",
        "webpack-cli": "^3.3.11",
        "webpack-dev-server": "^3.10.3",
        "webpack-merge": "^4.2.2",
        "@babel/core": "^7.8.4",
        "@babel/plugin-proposal-class-properties": "^7.8.3",
        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
        "@babel/preset-env": "^7.8.4",
        "@babel/preset-react": "^7.8.3",
        "autoprefixer": "^9.7.4",
        "babel-loader": "^8.0.6",
        "clean-webpack-plugin": "^3.0.0",
        "css-loader": "^3.4.2",
        "cssnano": "^4.1.10",
        "file-loader": "^5.1.0",
        "friendly-errors-webpack-plugin": "^1.7.0",
        "html-webpack-externals-plugin": "^3.8.0",
        "html-webpack-plugin": "^3.2.0",
        "less": "^3.11.1",
        "less-loader": "^5.0.0",
        "mini-css-extract-plugin": "^0.9.0",
        "optimize-css-assets-webpack-plugin": "^5.0.3",
        "postcss-loader": "^3.0.0",
        "px2rem-loader": "^0.1.9",
        "react": "^16.13.0",
        "react-dom": "^16.13.0",
        "style-loader": "^1.1.3",
        "url-loader": "^3.0.0"
    }
}
```

冒烟测试

冒烟测试执行

  1. 构建是否成功
  2. 每次构建完成的build目录是否由内容输出
    • 是否有js、css等静态资源文件
    • 是否有HTML文件
  3. 在示例项目里面运行构建,看看是否有报错
  4. 判断基本功能是否正常
    • 编写 mocha 测试用例
    • 是否有js、css等静态资源文件
    • 是否有HTML文件

开始 mocha 冒烟测试

  1. 在测试文件夹操作
    • 创建文件夹:./test/smoke
    • 创建文件:./test/smoke/index.js
    • 创建文件夹:./test/smoke/template
      • 在这里面拷贝一个项目
  2. 编辑文件:./test/smoke/index.js
    • 安装插件:npm i -D rimraf。 构建前用来删除dist目录
    const path = require('path')
    const webpack = require('webpack')
    const rimraf = require('rimraf')    // 删除文件的插件
    const Mocha = require('mocha')  // 编写测试用例的插件
    
    const mocha = new Mocha({
        timeout: '10000ms'
    })
    
    // 当前进程的工作目录
    process.chdir(path.join(__dirname, 'template')) // process.chdir() 方法变更 Node.js 进程的当前工作目录
    
    // 删除dist目录
    rimraf('./dist', () => {
        // 获取构建配置
        const prodConfig = require('../../lib/webpack.prod.js')
        // 运行配置
        webpack(prodConfig, (err, stats) => {
            // 运行失败
            if(err) {
                console.err(err);
                process.exit(2)
            }
            // 运行成功
            console.log(stats.toString({
                colors: true,
                modules: false,
                children: false
            }));
            // 加入测试用例
            console.log('webpack build success, begin run test');
            mocha.addFile(path.join(__dirname, 'html-test.js'))
            mocha.addFile(path.join(__dirname, 'css-js-test.js'))
            mocha.run()
        })
    })
  3. 运行当前编写的文件: node ./test/smoke/index.js

编写测试用例

  1. 安装插件:npm i mocha -D
  2. 新建文件:./smoke/html-test.js
    • 用来检查是否存在html文件:npm i -D glob-all
    const glob = require('glob-all')
    // 检测是否存在html文件
    describe('Checking generated html files', () => {
        it('should generate html files', (done) => {
            // 同步判断文件是否生成了出来
            const files = glob.sync([
                './dist/index.html',
                './dist/search.html',
            ])
            if (files.length > 0) {
                done()
            } else {
                throw new Error('no html files generate!')
            }
        })
    })
  3. 新建文件:./smoke/css-js-test.js
    • 用来检查是否存在css/js文件
    const glob = require('glob-all')
    // 检测是否存在html文件
    describe('Checking generated css js files', () => {
        it('should generate css js files', (done) => {
            // 同步判断文件是否生成了出来
            const files = glob.sync([
                './dist/js/index_*.html',
                './dist/js/search_*.html',
                './dist/css/index_*.css',
                './dist/css/search_*.css',
            ])
            if (files.length > 0) {
                done()
            } else {
                throw new Error('no css js files generate!')
            }
        })
    })

单元测试与测试覆盖率

单元测试描述

  1. 技术选型:mocha + chai
  2. 测试代码:descript, it, expect
  3. 测试命令:mocha add.test.js
    const expect = require('chai').expect
    const add = require('../src/add')
    descripe('use expect:src/add.js', () => {
        it('add(1,2) === 3', () => {
            expect(add(1, 2).to.equal(3))
        })
    })

单元测试接入

  1. 安装 mocha + chai
    • npm i mocha chai -D
  2. 新建test目录,并增加xxx.test.js测试文件
  3. 修改package.json文件
    "script": {
        "test": "./node_modules/.bin/_mocha"
    }
  4. 执行测试命令
    • npm run test
  5. 创建文件夹/文件
    • ./test/unit/webpack-base-test.js
    const assert = require('assert')
    describe('webpack.base.js test case', () => {
        const baseConfig = require('../../lib/webpack.base')
        it('entry', () => {
            assert.equal(baseConfig(true).entry.index.includes('/test/smoke/template/src/index/index.js'), true)
            assert.equal(baseConfig(true).entry.search.includes('/test/smoke/template/src/search/index.js'), true)
        })
    })
    • ./text.index
    const path = require('path')
    // 进入模板目录
    process.chdir(path.join(__dirname, 'smoke/template' ))
    describe('builder-webpack test case', () => {
        require('./unit/webpack-base-test')
        // ... require其他文件
    })
  6. 运行测试
    • npm run test

测试覆盖率(下面的代码测试失败)

  1. 安装插件: npm i -g istanbul
  2. 修改package.json文件
    "script": {
        "test": "istanbul cover ./node_modules/.bin/_mocha"
    }

持续集成和Travis-CI

  1. 优点:快速发现错误,防止分支大幅偏离主干(每次提交前都会运行测试)
  2. 核心措施:代码集成到主干前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
  3. 接入Travis-CI
    • https://travis-ci.org 使用GitHub账号
    • 在https://travis-ci.org/account/repositories 为项目开启
    • 项目根目录下新增:.travis.yml

发布到npm

  1. npm搜索是否存在此包
  2. 如果发布之前需要执行某件事情
    • 修改package.json文件
    "script": {
        "prepare": "webpack"
    }
  3. 发布:npm publish
  4. 如果本机第一次发布包(非第一次可忽略)
    • 在终端输入npm adduser,提示输入账号,密码和邮箱,然后将提示创建成功
  5. 非第一次发布包
    • 在终端输入npm login,然后输入你创建的账号和密码,和邮箱,登陆,结果上
  6. 如何撤销发布的包
    • 终端执行: npm unpublish
    • 删除某个版本: npm unpublish [email protected]
    • 删除整个npm市场的包: npm unpublish z-tool --force
  7. 登录
    • npm login
  8. 注意
    • 发包前必须取消webpack.config.js中的文件监听模式
  9. 更新package.json的小版本号:npm version patch

webpack 分析

  1. 初级分析:使用webpack内置的stats(颗粒度比较粗)
    "script": {
        "build:stats": "webpack  --config ./lib/webpack.prod.js --json > stats.json"
    }
  2. 速度分析:使用npm i -D speed-measure-webpack-plugin
    • 分析整个打包总耗时
    • 每个插件和loader的耗时情况
    • 编辑:./lib.webpack.prod.js
      const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin') // 分析打包速度
      const smp = new SpeedMeasureWebpackPlugin()
      module.exports = smp.wrap(merge(baseConfig(false), prodConfig)) // module.exports = smp.wrap(/*webpack配置对象*/)
  3. 体积分析
    • 分析项目体积:使用npm i -D webpack-bundle-analyzer
    • 编辑:./lib.webpack.prod.js
    const WebpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;  // 分析打包体积
    // 插件
    plugins: [
        new WebpackBundleAnalyzer(),  // 分析打包体积
    ],
    • 会自动打开:http://127.0.0.1:8888/

优化webpack构建

多进程/多实例:使用three-loader解析资源(效果不大,最终未使用)

  1. 安装:npm i -D thread-loader
  2. 修改:webpack.prod.js
    module: {
        rules: [
        {
            test: /\.js$/,
            use: [
            // { loader: 'thread-loader',  options: { workers: 3 } },  // 多进程打包
            'babel-loader',
            // 'eslint-loader', // 添加eslint
            ],
        },
        ],
    },

多进程/多实例“并行压缩 (效果不大,最终未使用)

  1. terser-webpack-plugin 开启parallel参数
  2. 修改:webpack.prod.js
    const TerserWebpackPlugin = require('terser-webpack-plugin');   // 多进程/多实例并行压缩
    optimization: {
        minimizer: [
            // new TerserWebpackPlugin({parallel: true})
        ]
    },

进一步分包:预编译资源模块(给基础库打包, 可以替代之前的optimization->splitChunks)

  1. 思路:
    • 将react,react-dom,redux,react-redux基础包和业务包打成一个文件
  2. 方法:
    • 使用DLLPlugin进行分包,
    • DllReferencePlugin对 manifest.json进行引用
  3. 创建文件:./lib/webpack.dll.js
  4. 修改文件:package.json
    "scripts": {
        "dll": "webpack --config ./lib/webpack.dll.js",
    },
  5. 后面没做了,自己看资料

缓存,提升二次构建速度

  1. 缓存思路
  2. babel-loader 开启缓存
    • 修改:webpack.base.js
    module: {
        rules: [
            {
            test: /\.js$/,
            use: ['babel-loader?cacheDirectory=true',
                // 'eslint-loader', // 添加eslint
            ],
            }
        ]
    }
  3. terser-webpack-plugin 开启缓存
    • 修改:webpack.prod.js
    const TerserPlugin = require('terser-webpack-plugin');
    module: {
        optimization: {
            minimizer: [new TerserPlugin({
                parallel: true, cache: true
            })],
        },
    }
  4. 使用cache-loader 或者 hard-source-webpack-plugin
    • 安装插件:npm i hard-source-webpack-plugin -D
    • 修改:webpack.prod.js
    const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');  // 用于打包缓存
    plugins: [
        new HardSourceWebpackPlugin(),    // 用于打包中的缓存
    ],

缩小构建目标

  1. 目的:尽可能少构建模块,比如:babel-loader 不解析node_modules
  2. 减少文件搜索范围
    • 优化 resolve.modules 配置(减少模块搜索层级)
    • 优化 resolve.mainField 配置
    • 优化 resolve.extensions 配置
    • 合理使用 alias

图片压缩(测试失败)

  1. 要求:基于 Node 库的imagemin 或者 tinypng API
  2. 使用:配置 image-webpack-loader
    • npm install image-webpack-loader --save-dev