资讯专栏INFORMATION COLUMN

用webpack写个现代的JavaScript包

yhaolpz / 747人阅读

摘要:这里我们可以尝试运行一下命令尝试能否构建成功,成功的情况下在目录下会生成的文件。在添加调试代码打开调试面板在下拉选项中选择添加配置或者直接创建并打开文件使用了解相关属性。

webpack 作为目前主流的构建工具,其较快的版本迭代和复杂的配置方式,使得每次开发前不得不规划相当部分时间来调试。这里将记录整个环境的搭建过程,为新手提供基础思路。

就像我在开发vue-sitemap时一样,构建工具往往需要达到下面几个需求:

构建生成 CommonJS/UMD/ES Modules 三种模式的代码提供给使用者

需运行测试和检查测试覆盖的进度

开发时候使用 VS Code 编辑器进行断点调试

以上三个作为开发一个组件(package)是基础中基础的需求,当然还有更多细节内容需要添加,由于篇幅过长另加文章再说吧。(欢迎各位读者评论留下你认为需要的功能( • ̀ω•́ )✧)

第一步:构建工具

接下来我们先从最基础的开始,需要安装 Node.js(10.x) 作为所有代码的运行环境, webpack 也是一样。

初始化项目

由于我需要把项目发布至 npm 的,使用命令初始化项目描述文件 package.json

npm init
初次化细节各位读者找其他文章补全吧,这里不细说

接下来看看目录结构

│  package.json     //项目描述文件
│  README.md        //GitHub创建仓库时默认创建
├─src               //源代码目录
│      index.js     //入口文件
├─tests             //测试代码目录
│
├─dist              //生产文件的目录
│
└─docs              //文档目录
添加 webpack
npm install -D webpack webpack-cli cross-env
//or
//yarn add webpack webpack-cli cross-env -D
这里使用的 webpack v4,后续设置也是基于4来设置,cross-env是帮助在 win 下能正常使用环境变量的包,我开发在 win 环境于是在这加上。

yarn 是一款快速、可靠、安全的依赖管理工具。如果你觉得 npm 安装时候较慢的话,不妨试试。

等依赖下载解决完毕之后,,在package.json设置构建命令方便之后使用。

//# package.json
{
  //...
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --propress --hide-modules",
  }
}

这里我们可以尝试运行一下命令npm run build尝试能否构建成功,成功的情况下在dist目录下会生成main.js的文件。

配置 webpack

创建 webpack.config.js文件来配置 webpack 。为满足我们的第一个需要生成三种模式的代码:

//# webpack.config.js

const package = require("./package.json")
const path = require("path")

const config = {
    entry: "./src/index.js",  //入口文件
    output: {                 //输出设置
        path: path.resolve(__dirname, "./dist"),
        filename: `${package.name}.js`
    },
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "./src")
        }
    }
}

if (process.env.NODE_ENV === "umd") {
    config.optimization = { minimize: false };
    config.output.library = package.name;
    config.output.libraryTarget = "umd2";
    config.output.filename = `${package.name}.js`;
}
if (process.env.NODE_ENV === "umd:min") {
    config.output.library = package.name;
    config.output.libraryTarget = "umd2";
    config.output.filename = `${package.name}.min.js`;
}
if (process.env.NODE_ENV === "es") {
    config.output.library = package.name;
    config.output.libraryTarget = "amd";
    config.output.filename = `${package.name}.es.js`;
}
if (process.env.NODE_ENV === "commonjs") {
    config.output.library = package.name;
    config.output.libraryTarget = "commonjs2";
    config.output.filename = `${package.name}.common.js`;
}

module.exports = config
添加构建命令

package.json 添加新的运行命令

//# package.json
{
  "version": "0.1.0",
  "name": "vscode-mocha-webpack-example",
  "description": "用于管理导航、面包屑及路由等基于vue的功能整合",
  "main": "./src/index.js",
  "scripts": {
    "build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
    "build:umd": "cross-env NODE_ENV=umd webpack --mode=production --progress --hide-modules",
    "build:umd:min": "cross-env NODE_ENV=umd:min webpack --mode=production --progress --hide-modules",
    "build:es": "cross-env NODE_ENV=es webpack --mode=production --progress --hide-modules",
    "build:commonjs": "cross-env NODE_ENV=commonjs webpack --mode=production --progress --hide-modules"
  }
  ...
}

运行npm run build就会 CommonJS/UMD/ES Modules 三种模式生成对应的文件。

大概是这样子:

├─dist
│    vscode-mocha-webpack-example.common.js
│    vscode-mocha-webpack-example.es.js
│    vscode-mocha-webpack-example.min.js
│    vscode-mocha-webpack-example.js
指定终端

为了使你的构建文件成为最终发布包的一部分,你必须声明它们。将以下内容添加到package.json:

"main": "dist/vscode-mocha-webpack-example.common.js",
"module": "dist/vscode-mocha-webpack-example.es.js",
"jsnext:main": "dist/vscode-mocha-webpack-example.es.js",
"files": [
  "dist",
  "src"
],

files部分告诉npm在发布时打包这些文件夹(否则,它们将被忽略,因为它们列在.gitignore文件中)

main定义CommonJS构建的终端

jsnext:mainmodule定义了ES2015构建的终端(我们定义了两个终端,因为jsnext:main是最早使用的规范,而module则更符合标准规范)。

第二步,设置babel

通过 babel 使得我们使用最新的语法,而不必担心运行环境不支持的问题。在webpack的下我们需要用到babel-loader来导入babel支持,关于最新的兼容设置还需使用上babel-preset-env

npm install -D babel babel-cli babel-preset-env
//or
//yarn add babel babel-cli babel-preset-env -D
创建 babel 配置文件

接着在.babelrc文件里设置babel兼容的规则:

{
    "presets": [
        [
            "env",
            {
                "useBuiltIns": false,
                "modules": false
            }
        ]
    ]
}
为 webpack 添加 babel-loader

当我们使用最新语法编写 JavaScript 时,webpack 会匹配将所有 JS 文件给 babel 的处理。

const package = require("./package.json")
const path = require("path")

const config = {
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname, "./dist"),
        filename: `${package.name}.js`
    },
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "./src")
        }
    },
    module: {
        rules: [
            {
                test: /.js$/,
                loader: "babel-loader",
                exclude: /node_modules/
            }
        ]
    }
}

...

module.exports = config

当运行构建时webpack便会加载babel及其相关的设置将代码转换并生成,到这步构建相关的设置基本完成。

第三步,添加自动化测试

相信对自动化测试有所了解的读者应该对mocha并不陌生,不了解的可以先去补补相关知识再往下看。简单的测试较多使用mocha来进行处理,还有断言库chai和提供promise支持的chai-as-promised等等,下面先把这些依赖安装上:

npm install -D mocha mocha-webpack chai chai-as-promised
//or
//yarn add mocha mocha-webpack chai chai-as-promised -D
测试代码想使用es新特性时可以使用mocha-webpack这个插件。

然后在package.json添加上测试命令:

{
    //...
    "scripts": {
        "build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
        "build:umd": "cross-env NODE_ENV=umd webpack --mode=production --progress --hide-modules",
        "build:umd:min": "cross-env NODE_ENV=umd:min webpack --mode=production --progress --hide-modules",
        "build:es": "cross-env NODE_ENV=es webpack --mode=production --progress --hide-modules",
        "test": "cross-env NODE_ENV=test mocha-webpack tests/**/*.spec.js"
    }
    //...
}

.babelrc 也需要设置一下:

{
    //...
    "env": {
        "test": {
            "presets": [
                [
                    "env",
                    {
                        "modules": false,
                        "targets": {
                            "node": "current"
                        }
                    }
                ]
            ]
        }
    }
}

为了能测试添加tests/unit/example.spec.jssrc/index.js两个文件,代码如下:

├─src
│      index.js
└─tests
    └─unit
            example.spec.js
//# src/index.js
export function getRole(user){
    switch(user){
        case "Packy":
            return "admin"
        case "Joan":
            return "reader"
    }
}

//# tests/unit/example.spec.js
import { assert } from "chai";
import { getRole } from "@/index";

describe("Testing", ()=>{
  it("Packy is admin", () => { assert.equal(getRole("Packy"), "admin") })
  it("Joan is reader", () => { assert.equal(getRole("Joan"), "reader") });
})

现在运行测试命令就能得出测试结果了:

npm run test

大概输出是这个样子:

 WEBPACK  Compiling...

  [=======================  ] 91% (additional chunk assets processing)
 WEBPACK  Compiled successfully in 5893ms

 MOCHA  Testing...



  Testing
    √ Packy is admin
    √ Joan is reader


  2 passing (39ms)

 MOCHA  Tests completed successfully
关于测试覆盖率的问题

有了测试还得知道测试是否都覆盖了所有代码(听说基本要到80%,有些团队可能要求更高90~95%),那如何得知?

nyc这个包就能帮助到我去检验测试覆盖率,首先先安装依赖:

npm install -D nyc babel-plugin-istanbul

再设置检查范围和添加命令:

//# package.json
{
    ...
    "scripts": {
        "build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
        "build:umd": "cross-env NODE_ENV=umd webpack --mode=production --progress --hide-modules",
        "build:umd:min": "cross-env NODE_ENV=umd:min webpack --mode=production --progress --hide-modules",
        "build:es": "cross-env NODE_ENV=es webpack --mode=production --progress --hide-modules",
        "build:commonjs": "cross-env NODE_ENV=commonjs webpack --mode=production --progress --hide-modules",
        "test": "cross-env NODE_ENV=test nyc mocha-webpack tests/**/*.spec.js"
    },
    ...
    "nyc": {
        "include": [
            "src/**"
        ],
        "instrument": false,
        "sourceMap": false
    }
    ...
}

安装依赖中也看到babel也需要添加相关的设置:

//# .babelrc
{
    ...
    "env": {
        "test": {
            "presets": [
                [
                    "env",
                    {
                        "modules": false,
                        "targets": {
                            "node": "current"
                        }
                    }
                ]
            ],
            "plugins": [
                "istanbul"
            ]
        }
    }
}

运行npm run test将会得到以下内容:

 WEBPACK  Compiling...

  [=======================  ] 91% (additional chunk assets processing)
 WEBPACK  Compiled successfully in 5893ms

 MOCHA  Testing...



  Testing
    √ Packy is admin
    √ Joan is reader


  2 passing (39ms)

 MOCHA  Tests completed successfully

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.js |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|

简单说一下这四栏东西代表什么意思:

Stmts : Statement coverage 声明覆盖率,程序中的每个语句都已执行吗?

Branch: Branch coverage 分支覆盖率,是否已执行每个控制结构的每个分支(也称为DD路径)(例如if和case语句)?例如,给定if语句,是否已执行true和false分支?

Funcs: Function coverage 方法覆盖率,是否已调用程序中的每个函数(或子例程)?

Lines: Line coverage 行代码覆盖,是否已执行源文件中的每个可执行的行?

不在覆盖范围内的代码的行数会在Uncovered Line这栏显示。

为测试提供async/await支持

在测试中想使用async/await语法,需新增setup.js文件并在入口处添加babel-polyfill

require("babel-polyfill");

并在.babelrc修改useBuiltInsentry

{
    ...
    "env": {
        "test": {
            "presets": [
                [
                    "env",
                    {
                        "useBuiltIns": "entry",
                        "modules": false,
                        "targets": {
                            "node": "current"
                        }
                    }
                ]
            ],
            "plugins": [
                "istanbul"
            ]
        }
    }
}

接下来在src/index.jstests/example.spec.js两个文件添加新的代码:

//# src/index.js
export function getUsers(){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            console.log("123")
            resolve(["Packy", "Joan"])
        }, 1000)
    })
}

//# tests/unit/example.spec.js
describe("GetUsers", ()=>{
  it("get result is Array", async ()=>{
    const users = await getUsers();
    assert.isArray(users, "[message]");
  })
})

运行测试就能看到效果!

让测试更进一步,在 VS Code 中调试

想在VS Code断点调试代码需要额外增加一些设置,添加以下代码至 webpack.config.js

//# webpack.config.js

//...

if (process.env.NODE_ENV === "test") {
    config.devtool = "eval-source-map";
    config.output = Object.assign(config.output, {
        devtoolModuleFilenameTemplate: "[absolute-resource-path]",
        devtoolFallbackModuleFilenameTemplate: "[absolute-resource-path]?[hash]"
    });
}

module.exports = config;
在VS Code 添加调试代码

打开 VS Code 调试面板在下拉选项中选择添加配置(或者直接创建并打开.vscode/launch.json文件):

// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
{
    "version": "0.2.0",
    "configurations": [
    {
        "type": "node",
        "request": "launch",
        "name": "Mocha-webpack Tests",
        "program": "${workspaceFolder}/node_modules/mocha-webpack/bin/mocha-webpack",
        "args": [
            "--full-trace",
            "--timeout",
            "999999",
            "--colors",
            "tests/**/*.js"
        ],
        "sourceMaps": true,
        "env": {
            "NODE_ENV": "test"
        },
        "internalConsoleOptions": "openOnSessionStart"
    }]
}

src目录下的源代码或是tests目录下的测试代码都能获得断点效果,想马上尝试可以下载本文例子vscode-mocha-webpack-example,安装依赖后就能尝试断点调试了。

设置参考源自vscode-ts-webpack-node-debug-example

值得一提的是,上面参考例子原文说devtool使用eval相关的设置并不能断点,但是在使用mocha-webpack调试时上面例子并不能实现断点。在我和公司小伙伴多番寻找vscodemocha-webpack的issue后,经过各种尝试下发现设置成eval-source-map便能实现最佳断点效果(eval也能实现但是由于断点sourcemap指向的源是生成后的文件导致在断点时多少会产生偏移)。

吐槽:在使用nvm切换nodejs环境时发现npm下载不了,打开github的下载链接直接404了,惊悚地发现npm整个搬走 (`Д´*)9 ┴┴,为解决这个问题请下载最新版本v1.1.7nvm

最后:

我的动力来自你的指头,请用你的指头使劲给我个赞吧!d(´ω` )

觉得本文有帮助的话不要忘记点一下收藏φ(>ω<*) 哦!

同时欢迎各路新手、大神在本文下方吐槽留言,谢谢参与讨论的各位仁兄!( • ̀ω•́ )✧

下面是本文完整例子,记得star一下!

vscode-mocha-webpack-example

同时非常感谢Mather协同编辑!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99427.html

相关文章

  • React全栈--现代前端技术

    摘要:类的数据类型就是函数,类本身就指向构造函数。另一个需要注意的地方是,在子类的构造函数中,只有调用之后,才可以使用关键字,否则会报错。 1.ES6-- 新一代JavaScript标准 1.1 const和let关键字 以前只有全局变量以及函数内部的局部变量,所以以前的局部变量只能定义在函数里面,但是现在不是了,可以通过let关键字来定义局部变量。同时通过const关键字来定义常量,定义后...

    youkede 评论0 收藏0
  • 现代脚本加载

    摘要:鉴于目前通行的做法就是在所有浏览器中一致同仁地加载,相比而言条件可以让大部分现代浏览器用户避免加载代码。 原文地址: Modern Script Loading, 文章作者是Preact作者Jason Miller showImg(https://segmentfault.com/img/remote/1460000019817095?w=1500&h=715); 背景知识 先简单...

    ranwu 评论0 收藏0
  • 2019-我前端面试题

    摘要:先说下我面试情况,我一共面试了家公司。篇在我面试的众多公司里,只有同城的面问到相关问题,其他公司压根没问。我自己回答的是自己开发组件面临的问题。完全不用担心对方到时候打电话核对的问题。 2019的5月9号,离发工资还有1天的时候,我的领导亲切把我叫到办公室跟我说:阿郭,我们公司要倒闭了,钱是没有的啦,为了不耽误你,你赶紧出去找工作吧。听到这话,我虎躯一震,这已经是第2个月没工资了。 公...

    iKcamp 评论0 收藏0
  • React文档翻译系列(一)安装

    摘要:文档翻译系列一安装原文地址原文本系列是针对文档进行的翻译,因为自己在学习的时候,最开始通过看博客或者论坛等中文资料,有些内容是零零散散的接收,并没有给自己带来很好的效果,所以后来决定把文档的原文从头到尾看一遍。 React文档翻译系列(一)安装 原文地址:原文 本系列是针对React文档进行的翻译,因为自己在学习react的时候,最开始通过看博客或者论坛等中文资料,有些内容是零零散散的...

    Fourierr 评论0 收藏0
  • 2020年如何写一个现代JavaScript

    摘要:我写过一些开源项目,在开源方面有一些经验,最近开到了阮老师的微博,深有感触,现在一个开源项目涉及的东西确实挺多的,特别是对于新手来说非常不友好最近我写了一个,旨在从多方面快速帮大家搭建一个标准的库,本文将已为例,介绍写一个开源库的知识 我写过一些开源项目,在开源方面有一些经验,最近开到了阮老师的微博,深有感触,现在一个开源项目涉及的东西确实挺多的,特别是对于新手来说非常不友好 show...

    joyqi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<