Webpack 工程化基础

webpack 基础

Wepack 工程打包机
Webpack 基本概念
Webpack Demos
Node.js Debugger
webpack学习实践系列
Webpack Dev Server
Webpack loader 十问

Webpack 总得来说是一个资源模块化 JS Module 打包工具,它的核心思想是模块化思想,不管你是图片,JS,CSS,SCSS,LESS,还是 JSX,统统都打包成 JS Module,Anythin to JS Modules。各种资源都有相适配的加载器 Loaders 负责对资源进行处理,Webpack 通过执行加载器完成原始资源的转换。

Hello Webpack

TypeScript
TypeScript Samples
TypeScript Node Starte
TS-Node
Webpack with TypeScript
Awesome TypeScript Loader
TypeScript loader for webpack
Babel loader for webpack
Base64 URL Loader
Webpack 资源管理
TypeScript声明文件 .d.ts
Build Performance

这里同过 TypeScript + Webpack 编写一个 Demo 工程来入门 Webpack 工程化打包机的理念,工程化是必然的软件技术发展方向。TypeScript 作为一个强大的静态类型检查语言是作为工程化开发的理想工具,所以 Node.js + TypeScript + Webpack + 框架的工程结构将会是非常流行的技术栈。TypeScript 现在发布 v3.5.3 版本,官方提供的示例 [TypeScript Node Starte] 是非常值得学习的案例。

Node.js 命令行的 TypeScript 编译器可以使用 npm 来安装,安装后会有一个 tsc 命令来转译 TypeScript 代码为 JavaScript,也可以安装 ts-node 来直接解析运行。

npm install --save-dev typescript@3.5.3 ts-node@8.3.0
tsc helloworld.ts
ts-node helloworld.ts

使用 VSCode、Sublime Text、Vim 作为开发工具都是很好的选择。

通过 Node.js 环境使用 Webpack,先通过 npm 来进行全局安装,创建示例工程 webpack-demo 目录,npm init 项目初始化命令生成默认的 package.json 配置文件,可以传入默许参数 --yes 忽略设置内容,关于它得用法可以查询 npm help init。一并安装 webpack-dev-server 这个开发用的 Web 服务器,还有 TypeScript 支持。

Webpack 处理 TypeScript (.ts) 原代码文件需要用到 ts-loaderawesome-typescript-loader,前者使用得更多。在代码中导入文件资源时需要 file-loader,导入图片时需要 url-loader,它可以对常用得图片进行 Base64编码,可以设置 limit 选项来限定待编码文件大小。在处理 CSS 样式文件时会用到 css-loader,打包后的样式在页面上还原出来时需要style-loader 提供的功能,它会在 <head> 节点下插入一个 <style> 节点,通过写入样式规则来还原经过打包的样式。

由于 TypeScript 静态类型的特殊性,那些非代码资源需要为 TypeScript 提供一个 Type Declaration 类型声明文件 .d.ts,在工程原代码目录下保存即可,TypeScript 会自动解析。如编写一个 .svg 资源的D类型声明文件:

// custom.d.ts
declare module "*.svg" {
  const content: any;
  export default content;
}

这个 SVG 类型声明模块指明,任何以 .svg 结尾的文件导入时将拥有一个 any 任意类型属性的 content,即数据部分是任意类型。还可以显式定义 url 属性为 string,即文件的地址。 这个De类型声明规则同样适用于 CSS, SCSS, JSON 等等,这是 TypeScript 静态类型系统特有的做法。

TypeScript 相比 JavaScript 增加了类型声明。这些类型声明帮助编译器识别类型,从而防止开发者搬起石头砸自己的脚。

原则上,TypeScript 需要开发者做到先声明后使用。这就导致开发者在调用很多原生接口(浏览器、Node.js)或者第三方模块的时候,因为某些全局变量或者对象的方法并没有声明过,导致编译器的类型检查失败。

用 ts 写的模块在发布的时候仍然是用 js 发布,这就导致一个问题:ts 那么多类型数据都没了,所以需要一个 d.ts 文件来标记某个 js 库里面对象的类型
然后 typings 就是一个网络上的 .d.ts 数据库。

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。它通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单的工具类。Lodash 的模块化方法 非常适用于做 array、object 和 string 的遍历操作,对值进行操作和检测,创建符合功能的函数等。

source-map-loader 是一个 SourceMap 代码地图工具,可以生成代码地图方便做调式。配置时可以在配置 devtool 设置内联 inline-source-mapsource-map 方式,前者会将调式代码嵌入打包输出,后者则独立保存为 .map 代码地图文件。

mkdir webpack-demo
cd webpack-demo
npm init --yes
npm install --save-dev webpack@4.1.1 webpack-cli@3.3.6 webpack-dev-server@3.7.2
npm install --save-dev file-loader@4.0.0 url-loader@2.0.1 ts-loader@6.0.4 css-loader@3.0.0 style-loader@0.23.1
npm install --save-dev awesome-typescript-loader@5.2.1
npm install --save-dev lodash@4.17.14 source-map-loader@0.2.4

配置文件参考,main 这里设置的式项目入口程序,如果已经准备好配置文件,直接执行 npm install 就可以根据配置好的依赖模块列表进行下载安装:

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "Webpack Getting Started",
  "main": "index.ts",
  "devDependencies": {
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.0.0",
    "file-loader": "^4.0.0",
    "html-webpack-plugin": "^3.2.0",
    "html-webpack-template": "^6.2.0",
    "lodash": "^4.17.14",
    "source-map-loader": "^0.2.4",
    "style-loader": "^0.23.1",
    "ts-loader": "^6.0.4",
    "ts-node": "^8.3.0",
    "typescript": "^3.5.3",
    "url-loader": "^2.0.1",
    "webpack": "^4.1.1",
    "webpack-cli": "^3.3.6",
    "webpack-dev-server": "^3.7.2"
  },
  "dependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --devtool eval --progress --colors",
    "build": "set NODE_ENV=production&& webpack -p"
  },
  "author": "Jeango",
  "license": "ISC"
}

需要注意的式,加载的模块愈多,就越消耗硬件资源,因此没有必要使用的组件就不要安装了,直接从配置文件中移除然后从新执行 npm install 安装即可更新配置。
例如给给规则指定 include 目录节省搜索时间,简化 resolve.modules, resolve.extensions, resolve.mainFiles, resolve.descriptionFiles。使用 DllPlugin 插件将那些不怎么修改的内容分离到另一个编译单元,避免在开发过程使用 source-mapminimize,特别是代码地图 Source maps 它非常耗资源,请考虑是否真的需要。

可以使用并行编译,parallel-webpackcache-loader 提供并行编译的能力,后者对性能开销较大的 loader 提供缓存服务。具体要点参考官方的构建优化文档[Build Performance]。

rules: [
  {
    test: /\.js$/,
    include: path.resolve(__dirname, 'src'),
    loader: 'babel-loader'
  }
]

webpack 命令的基本用法

webpack             – building for development
webpack -p          – building for production (minification)
webpack --watch     – for continuous incremental building
webpack -d          – including source maps
webpack --colors    – making building output pretty

为了方便使用打包命令,可以配置到 package.json 中的 scripts,例如要运行开发服务器 webpack-dev-server 只需要执行 npm run dev,要生成发布打包就执行 npm run build,另外 VSCode 对这里设置的命令配置项支持很到位,直接通过 VSCode 的 Terminal 菜单 Run Task 就可以执行。Sublime

{
  "scripts": {
    "dev": "webpack-dev-server --devtool eval --progress --colors",
    "build": "NODE_ENV=production webpack -p"
  },
}

接下来需要一个 TypeScript 配置文件 tsconfig.json,因为 TypeScript 需要使用到 Node.js 的类型信息,模块解析moduleResolution 设置成 node 模式。其它配置项信息可以参考官方文档 [TypeScript]。

{
    "compilerOptions": {
        "outDir": "./dist/", // path to output directory
        "sourceMap": true, // allow sourcemap support
        "strictNullChecks": true, // enable strict null checks as a best practice
        "module": "es6", // specifiy module code generation
        "target": "es5", // specify ECMAScript target version
        "allowJs": true, // allow a partial TypeScript and JavaScript codebase  
        "moduleResolution": "node",
        "noImplicitAny": true, // disallow implicit any type
        "noImplicitReturns": true,
        "strict": true,
    },
    "include": ["./src/"]
}

安装各种 Loaders 后,需要根据 Loader 开发文档参考配置,Webpack 得配置文件是 webpack.config.js 官方文档有很详细得说明。对于 Loader,主要是配置 rules 规则,test 是文件名匹配正则规则,符合匹配条件得文件就交给指定的 loader 进行处理,各个 Loader 的配置选项参考文档设置。

其中 entryoutput 是比较重要的配置,entry 表示程序入口,output 表示打包出口,即打包生成的文件。publicPath 是访问资源时使用的参考路径,打包后的资源存放路径与它直接关联。在使用开发服务器时,它就是项目的根目录下的路径,使用相对目录时要参考根目录来设置。output 还可以设置 path 属性来指定打包文件存放位置,默认是 dist 目录。结合 filename 指定输出文件就是 /dist/bin/bundle.js。如果对资源文件的发布目录有自定义需求,可以通过 process.env.NODE_ENV 变量判端是否是发布编译,然后再指定一个 publicPath 目录。

在 Webpack 4 中,不再强制要求指定 entry 和 output 路径。webpack 4 会默认 entry 为 ./src,output 为 ./dist

mode 模式设置,基本上有 development, production, none 几种模式。根据不同的模式使用不同的配置文件来优化开发/发布。

resolve 设定要解析的文件类型,设置错误解析不到的文件类型会产生 Module not found: Error: Can't resolve...

module.exports = {
    // change to .tsx if necessary
    entry: './src/index.ts',
    mode: 'development',
    output: {
        publicPath: "/",
        filename: './bin/bundle.js'
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".jsx"]
    },
    module: {
        rules: [{
            test: /\.(t|j)sx?$/,
            use: {
                loader: 'ts-loader'
            }
        }, {
            test: /\.(png|jpg|gif)$/i,
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 8192,
                },
            }, ],
        }, {
            test: /\.css$/i,
            use: [{
                loader: "style-loader"
            }, {
                loader: 'css-loader',
                options: {
                    modules: true,
                }
            }]
        }]
    // },
    // devtool: 'inline-source-map',
    // devtool: "source-map",
    // optimization: {
    //     minimize: true
    // },
    // externals: {
    //     "react": "React",
    //     "react-dom": "ReactDOM",
    }
}
if (process.env.NODE_ENV === "production") {
    module.exports.output.publicPath = "./release";
}

如果工程有多个主程序入口文件,那么可以将 entryoutput 修改成分组打包方式。Webpack 的输出参数 output 指定规则生成输出文件。所有的入口产生的输出文件都必须使用这一套规则,不能针对某一个特定的入口来制定 output 规则。输出项中用 [name] 来引用 entry每一项中的键值,用以批量指定生成后文件的名称。[hash] 引用本次编译的一个hash版本号,[chunkhash] 引用的是当前chunk的一个hash版本。也就是说,在同一次编译中,每一个chunk的hash都是不一样的;而在两次编译中,如果某个chunk根本没有发生变化,那么该chunk的hash也就不会发生变化。

html-webpack-pluginhtml-webpack-template 是两个生成 HTML 模板的插件,为了对发布目录 dist 自动清理,可以使用 clean-webpack-plugin 插件。这几个插件都是 Webpack 提供学习如何在 Node.js 平台下做 Webpack 插件开发用的,也具有一定的实用。

Node.js 提供的内置模块 path 可以用来解析绝对路径。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  // entry: {
  //   home: ['./home.js', './home.scss'],
  //   account: ['./account.js', './account.scss']
  // },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'Output Management'
    })
  ]
};

先准备一个页面模板 index.html 用它来加载 Webpack 打包生成的输出文件 bundle.js,为了简化这里就不引用第三方 JavaScript 库:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>TypeScript with Webpack</title>
        <style>
            .frame {
                width:50%;
                padding:10%;
                color:white;
                background: #282828;
            }
        </style>
    </head>
    <body>
        <div id="content" class="frame"></div>
        <script src="./bin/bundle.js"></script>
    </body>
</html>

新建 src 目录用来放源代码 index.ts

class Student {
    fullName: string = "";
    readonly age:number = 18;
    constructor( public firstName:string, public middle:string, public lastName:string){
        this.fullName = firstName + ' ' + middle + ' ' + lastName;
    }
}

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person) {
    return "Hello, " + person.firstName;
}

let user = { firstName: "Jane", lastName: "User" };
// let user = new Student("Jane", "M.", "User");

let $ = (id:string, msg:string) => {
    let tag = document.getElementById(id);
    if (!tag){
        document.body.innerHTML = ("HTML element not found #"+id);
    }else{
        tag.innerHTML = msg;
    }
}
$("content", greeter(user));

其它不需要打包的资源文件单独放放到 public 子目录下,这样的工程目录结构是比较通用合理的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • webpack使用学习 本分享学习借鉴webpack中文官网,官网链接(中文文档):https://www.web...
    腿毛怪丶叔叔阅读 834评论 0 5
  • webpack 是什么? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(mo...
    IT老马阅读 3,280评论 2 27
  • 一、概念 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bun...
    Timmy小石匠阅读 1,620评论 0 29
  • 全局安装webpack 全局安装webpack会有个问题,就是当你有两个项目依赖于不同版本的webpack,就会有...
    説好的妹紙呢阅读 1,760评论 0 11
  • 当生活被时间打碎 开始无止境的忙碌起来 再也没有空闲遐想 再也没有功夫小憩 我们开始遗忘 开始迷失 停下来 静坐一...
    花开云想阅读 414评论 0 1